diff --git a/.cirrus.yml b/.cirrus.yml index 214dda4ea1..8e02cc722d 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -234,10 +234,6 @@ promote_task: # Promotion depends_on: - build - - test_linux - - test_windows - - mend_scan - - qa <<: *ONLY_IF_EXCEPT_NIGHTLY eks_container: <<: *CONTAINER_DEFINITION diff --git a/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/AnalysisContainer.java b/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/AnalysisContainer.java index 2012e4ae21..e59f1ce073 100644 --- a/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/AnalysisContainer.java +++ b/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/AnalysisContainer.java @@ -42,6 +42,7 @@ import org.sonarsource.sonarlint.core.analysis.container.analysis.sensor.SonarLintSensorStorage; import org.sonarsource.sonarlint.core.analysis.container.global.AnalysisExtensionInstaller; import org.sonarsource.sonarlint.core.analysis.sonarapi.DefaultSensorContext; +import org.sonarsource.sonarlint.core.analysis.sonarapi.noop.NoOpAnalysisWarnings; import org.sonarsource.sonarlint.core.analysis.sonarapi.noop.NoOpFileLinesContextFactory; import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger; import org.sonarsource.sonarlint.core.commons.progress.ProgressIndicator; @@ -65,6 +66,7 @@ protected void doBeforeStart() { private void addCoreComponents() { add( + new NoOpAnalysisWarnings(), cancelMonitor, SonarLintInputProject.class, NoOpFileLinesContextFactory.class, diff --git a/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/filesystem/SonarLintFileSystem.java b/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/filesystem/SonarLintFileSystem.java index e668475240..e9ae3e9dcf 100644 --- a/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/filesystem/SonarLintFileSystem.java +++ b/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/container/analysis/filesystem/SonarLintFileSystem.java @@ -22,6 +22,7 @@ import java.io.File; import java.nio.charset.Charset; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.SortedSet; import java.util.stream.StreamSupport; import org.sonar.api.batch.fs.FilePredicate; @@ -140,7 +141,7 @@ public SortedSet languages() { @Override public File resolvePath(String path) { - throw new UnsupportedOperationException("resolvePath"); + return Paths.get(path).toFile(); } } diff --git a/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/sonarapi/noop/NoOpAnalysisWarnings.java b/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/sonarapi/noop/NoOpAnalysisWarnings.java new file mode 100644 index 0000000000..d2a2626037 --- /dev/null +++ b/backend/analysis-engine/src/main/java/org/sonarsource/sonarlint/core/analysis/sonarapi/noop/NoOpAnalysisWarnings.java @@ -0,0 +1,30 @@ +/* + * SonarLint Core - Analysis Engine + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT 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 02110-1301, USA. + */ +package org.sonarsource.sonarlint.core.analysis.sonarapi.noop; + +import org.sonar.api.notifications.AnalysisWarnings; + +public class NoOpAnalysisWarnings implements AnalysisWarnings { + + @Override + public void addUnique(String s) { + // no-op + } +} diff --git a/backend/plugin-commons/src/main/java/org/sonarsource/sonarlint/core/plugin/commons/PluginsLoader.java b/backend/plugin-commons/src/main/java/org/sonarsource/sonarlint/core/plugin/commons/PluginsLoader.java index 66f8b54921..9fb6d42c55 100644 --- a/backend/plugin-commons/src/main/java/org/sonarsource/sonarlint/core/plugin/commons/PluginsLoader.java +++ b/backend/plugin-commons/src/main/java/org/sonarsource/sonarlint/core/plugin/commons/PluginsLoader.java @@ -81,6 +81,8 @@ private static Set additionalAllowedPlugins(Configuration configuration) allowedPluginsIds.add("omnisharp"); allowedPluginsIds.add("iacenterprise"); allowedPluginsIds.add("goenterprise"); + allowedPluginsIds.add("security"); + allowedPluginsIds.add("securityjavafrontend"); allowedPluginsIds.addAll(maybeDbdAllowedPlugins(configuration.enableDataflowBugDetection)); return Collections.unmodifiableSet(allowedPluginsIds); } diff --git a/backend/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java b/backend/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java index c4cc97b9d2..83ffab9880 100644 --- a/backend/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java +++ b/backend/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java @@ -41,6 +41,8 @@ public class PluginsSynchronizer { public static final Version ENTERPRISE_GO_MIN_SQ_VERSION = Version.create("2025.2"); public static final String CSHARP_ENTERPRISE_PLUGIN_ID = "csharpenterprise"; private static final String GO_ENTERPRISE_PLUGIN_ID = "goenterprise"; + private static final String SECURITY_PLUGIN_ID = "security"; + private static final String SECURITY_JAVA_FRONTEND_PLUGIN_ID = "securityjavafrontend"; private static final SonarLintLogger LOG = SonarLintLogger.get(); private final Set sonarSourceDisabledPluginKeys; @@ -58,6 +60,10 @@ public PluginsSynchronizer(Set enabledLanguages, ConnectionStorag // SLCORE-1179 Force synchronize "C# Enterprise" after repackaging (SQS 10.8+) this.notSonarLintSupportedPluginsToSynchronize.add(CSHARP_ENTERPRISE_PLUGIN_ID); } + if (enabledLanguages.contains(SonarLanguage.JAVA)) { + this.notSonarLintSupportedPluginsToSynchronize.add(SECURITY_PLUGIN_ID); + this.notSonarLintSupportedPluginsToSynchronize.add(SECURITY_JAVA_FRONTEND_PLUGIN_ID); + } this.storage = storage; this.embeddedPluginKeys = embeddedPluginKeys; } diff --git a/medium-tests/pom.xml b/medium-tests/pom.xml index bc141c1213..d291bbc6c6 100644 --- a/medium-tests/pom.xml +++ b/medium-tests/pom.xml @@ -219,6 +219,18 @@ 1.36.1.13250 jar + + com.sonarsource.security + sonar-security-plugin + 11.5.0.38524 + jar + + + com.sonarsource.security + sonar-security-java-frontend-plugin + 11.5.0.38524 + jar + ${project.build.directory}/plugins false diff --git a/medium-tests/src/test/java/mediumtest/SecurityMediumTest.java b/medium-tests/src/test/java/mediumtest/SecurityMediumTest.java new file mode 100644 index 0000000000..57c033faff --- /dev/null +++ b/medium-tests/src/test/java/mediumtest/SecurityMediumTest.java @@ -0,0 +1,73 @@ +/* + * SonarLint Core - Medium Tests + * Copyright (C) 2016-2025 SonarSource SA + * mailto:info AT sonarsource DOT 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 02110-1301, USA. + */ +package mediumtest; + +import java.io.File; +import java.util.List; +import java.util.Map; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidOpenFileParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedFindingDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; +import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; +import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; +import utils.TestPlugin; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +public class SecurityMediumTest { + private static final String CONFIG_SCOPE_ID = "CONFIG_SCOPE_ID"; + private static final boolean COMMERCIAL_ENABLED = System.getProperty("commercial") != null; + + @SonarLintTest + void it_should_find_taint_issues(SonarLintTestHarness harness) { + assumeTrue(COMMERCIAL_ENABLED); + var projectWithTaint = new File("src/test/projects/project-with-taint").getAbsoluteFile().toPath(); + var srcFilePath = projectWithTaint.resolve("src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00008.java"); + var fileUri = srcFilePath.toUri(); + var fakeClient = harness.newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, projectWithTaint, List.of(new ClientFileDto(fileUri, projectWithTaint.relativize(srcFilePath), CONFIG_SCOPE_ID, false, + null, srcFilePath, null, null, true))) + .build(); + var backend = harness.newBackend() + .withUnboundConfigScope(CONFIG_SCOPE_ID, "My Project 1") + .withStandaloneRuleConfig("javasecurity:S3649", true, Map.of()) + .withStandaloneEmbeddedPlugin(TestPlugin.JAVA) + .withStandaloneEmbeddedPlugin(TestPlugin.SECURITY) + .withStandaloneEmbeddedPluginAndEnabledLanguage(TestPlugin.SECURITY_JAVA_FRONTEND) + .start(fakeClient); + fakeClient.setInferredAnalysisProperties(CONFIG_SCOPE_ID, Map.of("sonar.java.libraries", + "/home/damien.urruty/.m2/repository/javax/javaee-api/7.0/javaee-api-7.0.jar")); + + backend.getFileService().didOpenFile(new DidOpenFileParams(CONFIG_SCOPE_ID, fileUri)); + + verify(fakeClient, timeout(4000).times(1)).raiseIssues(eq(CONFIG_SCOPE_ID), any(), eq(false), any()); + var raisedIssuesForScopeId = fakeClient.getRaisedIssuesForScopeId(CONFIG_SCOPE_ID); + assertThat(raisedIssuesForScopeId) + .containsOnlyKeys(fileUri); + assertThat(raisedIssuesForScopeId.get(fileUri)) + .extracting(RaisedFindingDto::getRuleKey) + .contains("javasecurity:S3649"); + } +} diff --git a/medium-tests/src/test/java/utils/PluginLocator.java b/medium-tests/src/test/java/utils/PluginLocator.java index 4ee1cbd3fa..7e92e44ffb 100644 --- a/medium-tests/src/test/java/utils/PluginLocator.java +++ b/medium-tests/src/test/java/utils/PluginLocator.java @@ -31,6 +31,14 @@ public class PluginLocator { public static final String SONAR_JAVA_SE_PLUGIN_JAR = "sonar-java-symbolic-execution-plugin-" + SONAR_JAVA_SE_PLUGIN_VERSION + ".jar"; public static final String SONAR_JAVA_SE_PLUGIN_JAR_HASH = "unused"; + public static final String SONAR_SECURITY_PLUGIN_VERSION = "11.5.0.38524"; + public static final String SONAR_SECURITY_PLUGIN_JAR = "sonar-security-plugin-" + SONAR_SECURITY_PLUGIN_VERSION + ".jar"; + public static final String SONAR_SECURITY_PLUGIN_JAR_HASH = "osef"; + + public static final String SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_VERSION = "11.5.0.38524"; + public static final String SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_JAR = "sonar-security-java-frontend-plugin-" + SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_VERSION + ".jar"; + public static final String SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_JAR_HASH = "osef"; + public static final String SONAR_DBD_PLUGIN_VERSION = "1.36.1.13250"; public static final String SONAR_DBD_PLUGIN_JAR = "sonar-dbd-plugin-" + SONAR_DBD_PLUGIN_VERSION + ".jar"; public static final String SONAR_DBD_PLUGIN_JAR_HASH = "unused"; @@ -64,6 +72,14 @@ public static Path getJavaPluginPath() { return getValidPluginPath(SONAR_JAVA_PLUGIN_JAR); } + public static Path getSecurityPluginPath() { + return getValidPluginPath(SONAR_SECURITY_PLUGIN_JAR); + } + + public static Path getSecurityJavaFrontendPluginPath() { + return getValidPluginPath(SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_JAR); + } + public static Path getJavaSePluginPath() { return getPluginPath(SONAR_JAVA_SE_PLUGIN_JAR); } diff --git a/medium-tests/src/test/java/utils/TestPlugin.java b/medium-tests/src/test/java/utils/TestPlugin.java index 61c78a71bf..c28ce7f33a 100644 --- a/medium-tests/src/test/java/utils/TestPlugin.java +++ b/medium-tests/src/test/java/utils/TestPlugin.java @@ -29,6 +29,9 @@ public class TestPlugin { public static final Plugin JAVA = new Plugin(Language.JAVA, PluginLocator.getJavaPluginPath(), PluginLocator.SONAR_JAVA_PLUGIN_VERSION, PluginLocator.SONAR_JAVA_PLUGIN_JAR_HASH); public static final Plugin JAVA_SE = new Plugin("javasymbolicexecution", Language.JAVA, PluginLocator.getJavaSePluginPath(), PluginLocator.SONAR_JAVA_SE_PLUGIN_VERSION, PluginLocator.SONAR_JAVA_SE_PLUGIN_JAR_HASH); + public static final Plugin SECURITY = new Plugin(Language.JAVA, PluginLocator.getSecurityPluginPath(), PluginLocator.SONAR_SECURITY_PLUGIN_VERSION, PluginLocator.SONAR_SECURITY_PLUGIN_JAR_HASH); + public static final Plugin SECURITY_JAVA_FRONTEND = new Plugin(Language.JAVA, PluginLocator.getSecurityJavaFrontendPluginPath(), PluginLocator.SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_VERSION, PluginLocator.SONAR_SECURITY_JAVA_FRONTEND_PLUGIN_JAR_HASH); + public static final Plugin DBD = new Plugin("dbd", Language.JAVA, PluginLocator.getDbdPluginPath(), PluginLocator.SONAR_DBD_PLUGIN_VERSION, PluginLocator.SONAR_DBD_PLUGIN_JAR_HASH); public static final Plugin DBD_JAVA = new Plugin("dbdjavafrontend", Language.JAVA, PluginLocator.getDbdJavaPluginPath(), PluginLocator.SONAR_DBD_JAVA_PLUGIN_VERSION, PluginLocator.SONAR_DBD_JAVA_PLUGIN_JAR_HASH); diff --git a/medium-tests/src/test/projects/project-with-taint/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00008.java b/medium-tests/src/test/projects/project-with-taint/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00008.java new file mode 100644 index 0000000000..d1a1266f11 --- /dev/null +++ b/medium-tests/src/test/projects/project-with-taint/src/main/java/org/owasp/benchmark/testcode/BenchmarkTest00008.java @@ -0,0 +1,64 @@ +/** + * OWASP Benchmark v1.2 + * + *

This file is part of the Open Web Application Security Project (OWASP) Benchmark Project. For + * details, please see https://owasp.org/www-project-benchmark/. + * + *

The OWASP Benchmark 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, version 2. + * + *

The OWASP Benchmark 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. + * + * @author Dave Wichers + * @created 2015 + */ +package org.owasp.benchmark.testcode; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet(value = "/sqli-00/BenchmarkTest00008") +public class BenchmarkTest00008 extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + doPost(request, response); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + // some code + response.setContentType("text/html;charset=UTF-8"); + + String param = ""; + if (request.getHeader("BenchmarkTest00008") != null) { + param = request.getHeader("BenchmarkTest00008"); + } + + // URL Decode the header value since req.getHeader() doesn't. Unlike req.getParameter(). + param = java.net.URLDecoder.decode(param, "UTF-8"); + + String sql = "{call " + param + "}"; + + try { + java.sql.Connection connection = null; + java.sql.CallableStatement statement = connection.prepareCall(sql); + java.sql.ResultSet rs = statement.executeQuery(); + rs.toString(); + + } catch (java.sql.SQLException e) { + response.getWriter().println("Error processing request."); + } + } +}