diff --git a/.cirrus/modules/qa.star b/.cirrus/modules/qa.star
index 7e3ba392f..9400c6883 100644
--- a/.cirrus/modules/qa.star
+++ b/.cirrus/modules/qa.star
@@ -14,7 +14,7 @@ load(
"mkdir_orchestrator_home_script",
)
-QA_PLUGIN_GRADLE_TASK = "its:plugin:tests:integrationTest"
+QA_PLUGIN_GRADLE_TASK = "its:plugin:integrationTest"
QA_RULING_GRADLE_TASK = "its:ruling:integrationTest"
QA_QUBE_LATEST_RELEASE = "LATEST_RELEASE"
diff --git a/README.md b/README.md
index e2ae9d0ae..c8ff329e7 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,7 @@ Before running any of the integration tests make sure the submodules are checked
#### Plugin Test
The "Plugin Test" is an additional integration test that verifies plugin features such as metric calculation, coverage, etc. To launch it:
```shell
-./gradlew its:plugin:tests:integrationTest
+./gradlew its:plugin:integrationTest
```
#### Ruling Test
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b9f9ace36..4c6a6fb39 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -4,8 +4,8 @@ sonar-plugin-api = "10.13.0.2560"
sonarqube = "10.7.0.96327"
sonar-scanner-gradle = "5.1.0.4882"
sonar-orchestrator = "5.0.0.2065"
-sonarlint = "9.8.0.76914"
-slf4j = "1.7.36"
+sonarlint = "10.9.0.79478"
+slf4j = "2.0.16"
sslr = "1.24.0.633"
junit = "5.11.3"
assertj = "3.26.3"
@@ -19,6 +19,7 @@ jsr305 = "3.0.2"
maven-project = "2.2.1"
junit-platform = "1.11.3"
staxmate = "2.0.1"
+awaitility = "4.2.2"
[libraries]
sonar-analyzer-commons = { module = "org.sonarsource.analyzer-commons:sonar-analyzer-commons", version.ref = "sonar-commons" }
@@ -32,8 +33,9 @@ sonar-testing-harness = { module = "org.sonarsource.sonarqube:sonar-testing-harn
sonar-ws = { module = "org.sonarsource.sonarqube:sonar-ws", version.ref = "sonarqube" }
sonar-scanner-gradle = { module = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin", version.ref = "sonar-scanner-gradle" }
sonar-orchestrator-junit5 = { module = "org.sonarsource.orchestrator:sonar-orchestrator-junit5", version.ref = "sonar-orchestrator" }
-sonarlint-core = { module = "org.sonarsource.sonarlint.core:sonarlint-core", version.ref = "sonarlint" }
-sonarlint-plugin-api = { module = "org.sonarsource.sonarlint.core:sonarlint-plugin-api", version.ref = "sonarlint" }
+sonar-lint-core = { module = "org.sonarsource.sonarlint.core:sonarlint-core", version.ref = "sonarlint" }
+sonar-lint-rpc-java-client = { module = "org.sonarsource.sonarlint.core:sonarlint-rpc-java-client", version.ref = "sonarlint" }
+sonar-lint-rpc-impl = { module = "org.sonarsource.sonarlint.core:sonarlint-rpc-impl", version.ref = "sonarlint" }
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
sslr-core = { module = "org.sonarsource.sslr:sslr-core", version.ref = "sslr" }
sslr-toolkit = { module = "org.sonarsource.sslr:sslr-toolkit", version.ref = "sslr" }
@@ -50,6 +52,7 @@ jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
maven-project = { module = "org.apache.maven:maven-project", version.ref = "maven-project" }
junit-platform = { module = "org.junit.platform:junit-platform-suite", version.ref = "junit-platform" }
staxmate = { module = "org.codehaus.staxmate:staxmate", version.ref = "staxmate" }
+awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility"}
[plugins]
spotless = { id = "com.diffplug.spotless", version.ref = "spotless-gradle" }
diff --git a/its/plugin/tests/build.gradle.kts b/its/plugin/build.gradle.kts
similarity index 76%
rename from its/plugin/tests/build.gradle.kts
rename to its/plugin/build.gradle.kts
index d882521ca..d0d063e5a 100644
--- a/its/plugin/tests/build.gradle.kts
+++ b/its/plugin/build.gradle.kts
@@ -7,15 +7,17 @@ plugins {
description = "PHP :: Integration Tests :: Plugin"
dependencies {
- "integrationTestImplementation"(project(":sonar-php-plugin", configuration = "shadow"))
+ "integrationTestCompileOnly"(project(":sonar-php-plugin", configuration = "shadow"))
"integrationTestImplementation"(libs.sonar.orchestrator.junit5)
- "integrationTestImplementation"(libs.junit.platform)
- "integrationTestImplementation"(libs.sonar.ws)
- "integrationTestImplementation"(libs.sonarlint.core)
- "integrationTestImplementation"(libs.sonarlint.plugin.api)
"integrationTestImplementation"(libs.sonar.plugin.api)
+ "integrationTestImplementation"(libs.sonar.ws)
+ "integrationTestImplementation"(libs.sonar.lint.core)
+ "integrationTestImplementation"(libs.sonar.lint.rpc.java.client)
+ "integrationTestImplementation"(libs.sonar.lint.rpc.impl)
+ "integrationTestImplementation"(libs.junit.platform)
"integrationTestImplementation"(libs.junit.jupiter)
"integrationTestImplementation"(libs.assertj.core)
+ "integrationTestImplementation"(libs.awaitility)
"integrationTestCompileOnly"(libs.jsr305)
}
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/CpdTokenTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/CpdTokenTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/CpdTokenTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/CpdTokenTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/CustomRulesTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/CustomRulesTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/CustomRulesTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/CustomRulesTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/FrameworkDetectionPHPTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/FrameworkDetectionPHPTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/FrameworkDetectionPHPTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/FrameworkDetectionPHPTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/NoSonarTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/NoSonarTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/NoSonarTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/NoSonarTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/NonPhpProjectTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/NonPhpProjectTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/NonPhpProjectTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/NonPhpProjectTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPIntegrationTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/PHPIntegrationTest.java
similarity index 98%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPIntegrationTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/PHPIntegrationTest.java
index 61685ba49..2380c27ea 100644
--- a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPIntegrationTest.java
+++ b/its/plugin/src/integrationTest/java/com/sonar/it/php/PHPIntegrationTest.java
@@ -48,7 +48,7 @@ class PHPIntegrationTest {
static void startServer() {
Tests.provisionProject(PROJECT_KEY, PROJECT_NAME, "php", "it-profile");
SonarScanner build = createScanner()
- .setProjectDir(FileLocation.of("../../sources/src/psysh/").getFile())
+ .setProjectDir(FileLocation.of("../sources/src/psysh/").getFile())
.setProjectKey(PROJECT_KEY)
.setProjectName(PROJECT_NAME)
.setSourceEncoding("UTF-8")
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/PHPTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/PHPTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPUnitTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/PHPUnitTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PHPUnitTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/PHPUnitTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PhpStanReportTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/PhpStanReportTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PhpStanReportTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/PhpStanReportTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PsalmReportTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/PsalmReportTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/PsalmReportTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/PsalmReportTest.java
diff --git a/its/plugin/src/integrationTest/java/com/sonar/it/php/SonarLintTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/SonarLintTest.java
new file mode 100644
index 000000000..544d1d170
--- /dev/null
+++ b/its/plugin/src/integrationTest/java/com/sonar/it/php/SonarLintTest.java
@@ -0,0 +1,182 @@
+/*
+ * SonarQube PHP Plugin
+ * Copyright (C) 2010-2024 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 com.sonar.it.php;
+
+import com.sonar.it.php.utils.MockSonarLintRpcClientDelegate;
+import com.sonar.it.php.utils.SonarLintUtils;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Stream;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.sonarsource.sonarlint.core.rpc.client.ClientJsonRpcLauncher;
+import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate;
+import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher;
+import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesResponse;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidUpdateFileSystemParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.HttpConfigurationDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto;
+import org.sonarsource.sonarlint.core.serverconnection.FileUtils;
+
+import static com.sonar.it.php.utils.SonarLintUtils.ENABLED_LANGUAGES;
+import static com.sonar.it.php.utils.SonarLintUtils.IT_CLIENT_INFO;
+import static com.sonar.it.php.utils.SonarLintUtils.IT_TELEMETRY_ATTRIBUTES;
+import static com.sonar.it.php.utils.SonarLintUtils.toMap;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Inspiration:
+ * sonarlint-core StandaloneTests.java
+ */
+public class SonarLintTest {
+ public static final Path BASE_DIR = Paths.get("projects/sonarlint");
+ private static final String CONFIG_SCOPE_ID = "my-ide-project-name";
+
+ @TempDir
+ private static Path sonarUserHome;
+
+ private static SonarLintRpcServer backend;
+ private static SonarLintRpcClientDelegate client;
+
+ @BeforeAll
+ static void prepare() throws IOException, ExecutionException, InterruptedException {
+ startBackend();
+ }
+
+ @AfterEach
+ void reset() {
+ ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear();
+ }
+
+ @AfterAll
+ static void cleanup() throws InterruptedException, ExecutionException, TimeoutException {
+ backend.shutdown().get(1_000, TimeUnit.MILLISECONDS);
+ FileUtils.deleteRecursively(sonarUserHome);
+ }
+
+ private static SonarLintRpcClientDelegate newMockSonarLintClient() {
+ return new MockSonarLintRpcClientDelegate();
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void shouldRaiseIssue(Path inputFile) {
+ var analyzeResponse = analyzeFile(CONFIG_SCOPE_ID, inputFile);
+
+ assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty();
+ // it could happen that the notification is not yet received while the analysis request is finished.
+ Awaitility.await().atMost(Duration.ofMillis(200)).untilAsserted(
+ () -> assertThat(((MockSonarLintRpcClientDelegate) client).getRaisedIssues(CONFIG_SCOPE_ID)).hasSize(3));
+ }
+
+ static List shouldRaiseIssue() {
+ return provideTestFiles();
+ }
+
+ private static List provideTestFiles() {
+ List testFiles;
+
+ try (Stream pathStream = Files.list(BASE_DIR)) {
+ testFiles = pathStream
+ .map(Path::getFileName)
+ .map(BASE_DIR::resolve)
+ .toList();
+ } catch (IOException e) {
+ throw new AssertionError("Can not load test files from " + BASE_DIR, e);
+ }
+
+ if (testFiles.isEmpty()) {
+ throw new AssertionError("There are no test files provided");
+ }
+
+ return testFiles;
+ }
+
+ private static AnalyzeFilesResponse analyzeFile(String configScopeId, Path filePath, String... properties) {
+ var fileUri = filePath.toUri();
+ backend.getFileService().didUpdateFileSystem(
+ new DidUpdateFileSystemParams(List.of(), List.of(
+ new ClientFileDto(fileUri, filePath, configScopeId, false, null, filePath.toAbsolutePath(), null, null, true))));
+
+ return backend.getAnalysisService().analyzeFiles(
+ new AnalyzeFilesParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), System.currentTimeMillis()))
+ .join();
+ }
+
+ static void startBackend() throws IOException, ExecutionException, InterruptedException {
+ var clientToServerOutputStream = new PipedOutputStream();
+ var clientToServerInputStream = new PipedInputStream(clientToServerOutputStream);
+
+ var serverToClientOutputStream = new PipedOutputStream();
+ var serverToClientInputStream = new PipedInputStream(serverToClientOutputStream);
+ client = newMockSonarLintClient();
+ new BackendJsonRpcLauncher(clientToServerInputStream, serverToClientOutputStream);
+ var clientLauncher = new ClientJsonRpcLauncher(serverToClientInputStream, clientToServerOutputStream, client);
+ backend = clientLauncher.getServerProxy();
+
+ var featureFlags = SonarLintUtils.featureFlagsForStandaloneMode();
+ backend.initialize(
+ new InitializeParams(
+ IT_CLIENT_INFO,
+ IT_TELEMETRY_ATTRIBUTES,
+ HttpConfigurationDto.defaultConfig(),
+ null,
+ featureFlags,
+ sonarUserHome.resolve("storage"),
+ sonarUserHome.resolve("work"),
+ Set.of(Tests.PHP_PLUGIN_LOCATION.getFile().toPath()),
+ emptyMap(),
+ ENABLED_LANGUAGES,
+ emptySet(),
+ emptySet(),
+ emptyList(),
+ emptyList(),
+ sonarUserHome.toString(),
+ Map.of(),
+ false,
+ null,
+ false,
+ null))
+ .get();
+ }
+}
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/SuppressWarningsTest.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/SuppressWarningsTest.java
similarity index 100%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/SuppressWarningsTest.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/SuppressWarningsTest.java
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/Tests.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/Tests.java
similarity index 96%
rename from its/plugin/tests/src/integrationTest/java/com/sonar/it/php/Tests.java
rename to its/plugin/src/integrationTest/java/com/sonar/it/php/Tests.java
index 1e4fb210f..1d2aaca37 100644
--- a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/Tests.java
+++ b/its/plugin/src/integrationTest/java/com/sonar/it/php/Tests.java
@@ -51,7 +51,7 @@
class Tests {
- public static final String PROJECT_ROOT_DIR = "../projects/";
+ public static final String PROJECT_ROOT_DIR = "projects/";
private static final String RESOURCE_DIRECTORY = "/com/sonar/it/php/";
@@ -59,7 +59,7 @@ class Tests {
public static final String PHP_INI_SENSOR_NAME = "Analyzer for \"php.ini\" files";
- public static final FileLocation PHP_PLUGIN_LOCATION = FileLocation.byWildcardFilename(new File("../../../sonar-php-plugin/build/libs"), "sonar-php-plugin-*-all.jar");
+ public static final FileLocation PHP_PLUGIN_LOCATION = FileLocation.byWildcardFilename(new File("../../sonar-php-plugin/build/libs"), "sonar-php-plugin-*-all.jar");
public static final String SCANNER_VERSION = "6.1.0.4477";
private static final Pattern DEBUG_AND_INFO_LOG_LINE_PATTERN = Pattern.compile("\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s(INFO|DEBUG)\\s.*");
@@ -74,7 +74,7 @@ class Tests {
.restoreProfileAtStartup(FileLocation.ofClasspath(RESOURCE_DIRECTORY + "drupal_profile.xml"))
.restoreProfileAtStartup(FileLocation.ofClasspath(RESOURCE_DIRECTORY + "no_rules.xml"))
// Custom rules plugin
- .addPlugin(FileLocation.byWildcardFilename(new File("../../../php-custom-rules-plugin/build/libs"), "php-custom-rules-plugin-*-all.jar"))
+ .addPlugin(FileLocation.byWildcardFilename(new File("../../php-custom-rules-plugin/build/libs"), "php-custom-rules-plugin-*-all.jar"))
.restoreProfileAtStartup(FileLocation.ofClasspath(RESOURCE_DIRECTORY + "profile-php-custom-rules.xml"))
.restoreProfileAtStartup(FileLocation.ofClasspath(RESOURCE_DIRECTORY + "nosonar.xml"))
.restoreProfileAtStartup(FileLocation.ofClasspath(RESOURCE_DIRECTORY + "sleep.xml"))
diff --git a/its/plugin/src/integrationTest/java/com/sonar/it/php/utils/MockSonarLintRpcClientDelegate.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/utils/MockSonarLintRpcClientDelegate.java
new file mode 100644
index 000000000..b09d3639e
--- /dev/null
+++ b/its/plugin/src/integrationTest/java/com/sonar/it/php/utils/MockSonarLintRpcClientDelegate.java
@@ -0,0 +1,233 @@
+/*
+ * SonarQube PHP Plugin
+ * Copyright (C) 2010-2024 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 com.sonar.it.php.utils;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CancellationException;
+import org.sonarsource.sonarlint.core.rpc.client.ConfigScopeNotFoundException;
+import org.sonarsource.sonarlint.core.rpc.client.ConnectionNotFoundException;
+import org.sonarsource.sonarlint.core.rpc.client.SonarLintCancelChecker;
+import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingSuggestionDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingResponse;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.NoBindingSuggestionFoundParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionResponse;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.ConnectionSuggestionDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.event.DidReceiveServerHotspotEvent;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.fix.FixSuggestionDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.HotspotDetailsDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.http.GetProxyPasswordAuthenticationResponse;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.http.ProxyDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.http.X509CertificateDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.IssueDetailsDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.message.MessageType;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.message.ShowSoonUnsupportedMessageParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.progress.ReportProgressParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.progress.StartProgressParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.smartnotification.ShowSmartNotificationParams;
+import org.sonarsource.sonarlint.core.rpc.protocol.client.telemetry.TelemetryClientLiveAttributesResponse;
+import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.common.Either;
+import org.sonarsource.sonarlint.core.rpc.protocol.common.TokenDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.common.UsernamePasswordDto;
+
+/**
+ * Inspiration:
+ * sonarlint-core MockSonarLintRpcClientDelegate.java
+ */
+public class MockSonarLintRpcClientDelegate implements SonarLintRpcClientDelegate {
+ private final Map> raisedIssues = new HashMap<>();
+
+ public List getRaisedIssues(String configurationScopeId) {
+ var issues = raisedIssues.get(configurationScopeId);
+ return issues != null ? issues : List.of();
+ }
+
+ public Map> getRaisedIssues() {
+ return raisedIssues;
+ }
+
+ @Override
+ public void didRaiseIssue(String configurationScopeId, UUID analysisId, RawIssueDto rawIssue) {
+ raisedIssues.computeIfAbsent(configurationScopeId, k -> new ArrayList<>()).add(rawIssue);
+ }
+
+ @Override
+ public void suggestBinding(Map> suggestionsByConfigScope) {
+
+ }
+
+ @Override
+ public void suggestConnection(Map> suggestionsByConfigScope) {
+
+ }
+
+ @Override
+ public void openUrlInBrowser(URL url) {
+
+ }
+
+ @Override
+ public void showMessage(MessageType type, String text) {
+
+ }
+
+ @Override
+ public void log(LogParams params) {
+ System.out.println(params);
+ }
+
+ @Override
+ public void showSoonUnsupportedMessage(ShowSoonUnsupportedMessageParams params) {
+
+ }
+
+ @Override
+ public void showSmartNotification(ShowSmartNotificationParams params) {
+
+ }
+
+ @Override
+ public String getClientLiveDescription() {
+ return "";
+ }
+
+ @Override
+ public void showHotspot(String configurationScopeId, HotspotDetailsDto hotspotDetails) {
+
+ }
+
+ @Override
+ public void showIssue(String configurationScopeId, IssueDetailsDto issueDetails) {
+
+ }
+
+ @Override
+ public void showFixSuggestion(String configurationScopeId, String issueKey, FixSuggestionDto fixSuggestion) {
+
+ }
+
+ @Override
+ public AssistCreatingConnectionResponse assistCreatingConnection(AssistCreatingConnectionParams params, SonarLintCancelChecker cancelChecker) throws CancellationException {
+ throw new CancellationException("Unsupported in ITS");
+ }
+
+ @Override
+ public AssistBindingResponse assistBinding(AssistBindingParams params, SonarLintCancelChecker cancelChecker) throws CancellationException {
+ throw new CancellationException("Unsupported in ITS");
+ }
+
+ @Override
+ public void startProgress(StartProgressParams params) throws UnsupportedOperationException {
+
+ }
+
+ @Override
+ public void reportProgress(ReportProgressParams params) {
+
+ }
+
+ @Override
+ public void didSynchronizeConfigurationScopes(Set configurationScopeIds) {
+
+ }
+
+ @Override
+ public Either getCredentials(String connectionId) throws ConnectionNotFoundException {
+ throw new ConnectionNotFoundException();
+ }
+
+ @Override
+ public List selectProxies(URI uri) {
+ return List.of(ProxyDto.NO_PROXY);
+ }
+
+ @Override
+ public GetProxyPasswordAuthenticationResponse getProxyPasswordAuthentication(String host, int port, String protocol, String prompt, String scheme, URL targetHost) {
+ return new GetProxyPasswordAuthenticationResponse("", "");
+ }
+
+ @Override
+ public boolean checkServerTrusted(List chain, String authType) {
+ return false;
+ }
+
+ @Override
+ public void didReceiveServerHotspotEvent(DidReceiveServerHotspotEvent params) {
+
+ }
+
+ @Override
+ public String matchSonarProjectBranch(String configurationScopeId, String mainBranchName, Set allBranchesNames, SonarLintCancelChecker cancelChecker)
+ throws ConfigScopeNotFoundException {
+ return mainBranchName;
+ }
+
+ public boolean matchProjectBranch(String configurationScopeId, String branchNameToMatch, SonarLintCancelChecker cancelChecker) throws ConfigScopeNotFoundException {
+ return true;
+ }
+
+ @Override
+ public void didChangeMatchedSonarProjectBranch(String configScopeId, String newMatchedBranchName) {
+
+ }
+
+ @Override
+ public TelemetryClientLiveAttributesResponse getTelemetryLiveAttributes() {
+ System.err.println("Telemetry should be disabled in ITs");
+ throw new CancellationException("Telemetry should be disabled in ITs");
+ }
+
+ @Override
+ public void didChangeTaintVulnerabilities(String configurationScopeId, Set closedTaintVulnerabilityIds, List addedTaintVulnerabilities,
+ List updatedTaintVulnerabilities) {
+
+ }
+
+ @Override
+ public List listFiles(String configScopeId) {
+ return List.of();
+ }
+
+ @Override
+ public void noBindingSuggestionFound(NoBindingSuggestionFoundParams params) {
+ }
+
+ @Override
+ public void didChangeAnalysisReadiness(Set configurationScopeIds, boolean areReadyForAnalysis) {
+
+ }
+
+ public void clear() {
+ raisedIssues.clear();
+ }
+}
diff --git a/its/plugin/src/integrationTest/java/com/sonar/it/php/utils/SonarLintUtils.java b/its/plugin/src/integrationTest/java/com/sonar/it/php/utils/SonarLintUtils.java
new file mode 100644
index 000000000..5df8b2917
--- /dev/null
+++ b/its/plugin/src/integrationTest/java/com/sonar/it/php/utils/SonarLintUtils.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube PHP Plugin
+ * Copyright (C) 2010-2024 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 com.sonar.it.php.utils;
+
+import com.google.common.base.Preconditions;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.ClientConstantInfoDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.FeatureFlagsDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.TelemetryClientConstantAttributesDto;
+import org.sonarsource.sonarlint.core.rpc.protocol.common.Language;
+
+import static java.util.Collections.emptyMap;
+
+public class SonarLintUtils {
+ public static final Set ENABLED_LANGUAGES = EnumSet.of(Language.PHP);
+ public static final ClientConstantInfoDto IT_CLIENT_INFO = new ClientConstantInfoDto("clientName", "integrationTests");
+ public static final TelemetryClientConstantAttributesDto IT_TELEMETRY_ATTRIBUTES = new TelemetryClientConstantAttributesDto(
+ "SonarLint ITs", "SonarLint ITs", "1.2.3", "4.5.6", emptyMap());
+
+ public static FeatureFlagsDto featureFlagsForStandaloneMode() {
+ return new FeatureFlagsDto(
+ true,
+ true,
+ true,
+ false,
+ true,
+ true,
+ false,
+ true,
+ false,
+ false);
+ }
+
+ public static Map toMap(String[] keyValues) {
+ Preconditions.checkArgument(keyValues.length % 2 == 0, "Must be an even number of key/values");
+ Map map = new HashMap<>();
+ var index = 0;
+ while (index < keyValues.length) {
+ var key = keyValues[index++];
+ var value = keyValues[index++];
+ map.put(key, value);
+ }
+ return map;
+ }
+}
diff --git a/its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/drupal_profile.xml b/its/plugin/src/integrationTest/resources/com/sonar/it/php/drupal_profile.xml
similarity index 100%
rename from its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/drupal_profile.xml
rename to its/plugin/src/integrationTest/resources/com/sonar/it/php/drupal_profile.xml
diff --git a/its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/no_rules.xml b/its/plugin/src/integrationTest/resources/com/sonar/it/php/no_rules.xml
similarity index 100%
rename from its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/no_rules.xml
rename to its/plugin/src/integrationTest/resources/com/sonar/it/php/no_rules.xml
diff --git a/its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/nosonar.xml b/its/plugin/src/integrationTest/resources/com/sonar/it/php/nosonar.xml
similarity index 100%
rename from its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/nosonar.xml
rename to its/plugin/src/integrationTest/resources/com/sonar/it/php/nosonar.xml
diff --git a/its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/profile-php-custom-rules.xml b/its/plugin/src/integrationTest/resources/com/sonar/it/php/profile-php-custom-rules.xml
similarity index 100%
rename from its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/profile-php-custom-rules.xml
rename to its/plugin/src/integrationTest/resources/com/sonar/it/php/profile-php-custom-rules.xml
diff --git a/its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/profile.xml b/its/plugin/src/integrationTest/resources/com/sonar/it/php/profile.xml
similarity index 100%
rename from its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/profile.xml
rename to its/plugin/src/integrationTest/resources/com/sonar/it/php/profile.xml
diff --git a/its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/sleep.xml b/its/plugin/src/integrationTest/resources/com/sonar/it/php/sleep.xml
similarity index 100%
rename from its/plugin/tests/src/integrationTest/resources/com/sonar/it/php/sleep.xml
rename to its/plugin/src/integrationTest/resources/com/sonar/it/php/sleep.xml
diff --git a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/SonarLintTest.java b/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/SonarLintTest.java
deleted file mode 100644
index 256810421..000000000
--- a/its/plugin/tests/src/integrationTest/java/com/sonar/it/php/SonarLintTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * SonarQube PHP Plugin
- * Copyright (C) 2010-2024 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 com.sonar.it.php;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.sonarsource.sonarlint.core.StandaloneSonarLintEngineImpl;
-import org.sonarsource.sonarlint.core.analysis.api.ClientInputFile;
-import org.sonarsource.sonarlint.core.client.api.common.analysis.Issue;
-import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneAnalysisConfiguration;
-import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneGlobalConfiguration;
-import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneSonarLintEngine;
-import org.sonarsource.sonarlint.core.commons.Language;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class SonarLintTest {
-
- @TempDir
- public static File tempDirectory;
-
- private static StandaloneSonarLintEngine sonarlintEngine;
-
- private static Path baseDir;
-
- @BeforeAll
- static void prepare() {
- StandaloneGlobalConfiguration sonarLintConfig = StandaloneGlobalConfiguration.builder()
- .addPlugin(Tests.PHP_PLUGIN_LOCATION.getFile().toPath())
- .addEnabledLanguage(Language.PHP)
- .setSonarLintUserHome(tempDirectory.toPath())
- .setLogOutput((formattedMessage, level) -> {
- /* Don't pollute logs */
- })
- .build();
- sonarlintEngine = new StandaloneSonarLintEngineImpl(sonarLintConfig);
- baseDir = tempDirectory.toPath();
- }
-
- @AfterAll
- static void stop() {
- sonarlintEngine.stop();
- }
-
- @Test
- void shouldRaiseIssues() throws IOException {
- Path filePath = Tests.projectDirectoryFor("sonarlint").toPath().resolve("Math.php");
- filePath = Files.copy(filePath, baseDir.resolve("Math.php"));
- ClientInputFile inputFile = prepareInputFile(filePath, false);
-
- List issues = new ArrayList<>();
- StandaloneAnalysisConfiguration configuration = StandaloneAnalysisConfiguration.builder()
- .setBaseDir(baseDir)
- .addInputFile(inputFile)
- .build();
- sonarlintEngine.analyze(configuration, issues::add, null, null);
-
- assertThat(issues).hasSize(3);
- }
-
- private ClientInputFile prepareInputFile(Path filePath, final boolean isTest) {
- return createInputFile(filePath, isTest);
- }
-
- private ClientInputFile createInputFile(final Path path, final boolean isTest) {
- return new ClientInputFile() {
-
- @Override
- public String getPath() {
- return path.toString();
- }
-
- @Override
- public boolean isTest() {
- return isTest;
- }
-
- @Override
- public Charset getCharset() {
- return StandardCharsets.UTF_8;
- }
-
- @Override
- public G getClientObject() {
- return null;
- }
-
- @Override
- public String contents() throws IOException {
- return new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
- }
-
- @Override
- public String relativePath() {
- return path.toString();
- }
-
- @Override
- public URI uri() {
- return path.toUri();
- }
-
- @Override
- public InputStream inputStream() throws IOException {
- return Files.newInputStream(path);
- }
-
- };
- }
-
-}
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S128.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S128.json
index 0d3c0b36a..3b3cc28e0 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S128.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S128.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "CLEAR"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1314.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1314.json
index 66bbaf090..d7557e6e9 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1314.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1314.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "CLEAR"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1451.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1451.json
index b6b4082f1..2ce435101 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1451.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1451.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "LAWFUL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1599.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1599.json
index f9c5245a9..75447271f 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1599.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1599.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "CLEAR"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1799.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1799.json
index d2f7fbc76..c3bdbb939 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1799.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S1799.json
@@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
- "RELIABILITY": "HIGH"
+ "RELIABILITY": "BLOCKER"
},
"attribute": "CONVENTIONAL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2007.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2007.json
index b0efbcee6..b92e54850 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2007.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2007.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "MODULAR"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2014.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2014.json
index 88295059e..e41704ab5 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2014.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2014.json
@@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
- "RELIABILITY": "HIGH"
+ "RELIABILITY": "BLOCKER"
},
"attribute": "LOGICAL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.html b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.html
index b29dcea16..10bb69236 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.html
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.html
@@ -8,7 +8,6 @@
Credentials should be stored outside of the code in a configuration file, a database, or a management service for secrets.
This rule flags instances of hard-coded credentials used in database and LDAP connections. It looks for hard-coded credentials in connection
strings, and for variable names that match any of the patterns from the provided list.
-It’s recommended to customize the configuration of this rule with additional credential words such as "oauthToken", "secret", …
Ask Yourself Whether
- Credentials allow access to a sensitive component like a database, a file storage, an API or a service.
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.json
index 84ca11321..c23d4c83a 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2068.json
@@ -3,7 +3,7 @@
"type": "SECURITY_HOTSPOT",
"code": {
"impacts": {
- "SECURITY": "HIGH"
+ "SECURITY": "BLOCKER"
},
"attribute": "TRUSTWORTHY"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2187.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2187.json
index 47d868df5..a50a1de06 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2187.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2187.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "TESTED"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2245.html b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2245.html
index 2cf0ec73d..56c9e4d5b 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2245.html
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2245.html
@@ -1,45 +1,55 @@
-Using pseudorandom number generators (PRNGs) is security-sensitive. For example, it has led in the past to the following vulnerabilities:
+PRNGs are algorithms that produce sequences of numbers that only approximate true randomness. While they are suitable for applications like
+simulations or modeling, they are not appropriate for security-sensitive contexts because their outputs can be predictable if the internal state is
+known.
+In contrast, cryptographically secure pseudorandom number generators (CSPRNGs) are designed to be secure against prediction attacks. CSPRNGs use
+cryptographic algorithms to ensure that the generated sequences are not only random but also unpredictable, even if part of the sequence or the
+internal state becomes known. This unpredictability is crucial for security-related tasks such as generating encryption keys, tokens, or any other
+values that must remain confidential and resistant to guessing attacks.
+For example, the use of non-cryptographic PRNGs has led to vulnerabilities such as:
When software generates predictable values in a context requiring unpredictability, it may be possible for an attacker to guess the next value that
-will be generated, and use this guess to impersonate another user or access sensitive information.
-As the rand()
and mt_rand()
functions rely on a pseudorandom number generator, it should not be used for
-security-critical applications or for protecting sensitive data.
+will be generated, and use this guess to impersonate another user or access sensitive information. Therefore, it is critical to use CSPRNGs in any
+security-sensitive application to ensure the robustness and security of the system.
+As the rand()
and mt_rand()
functions are no CSPRNGs, they should not be used for security-critical applications or for
+protecting sensitive data.
Ask Yourself Whether
- the code using the generated value requires it to be unpredictable. It is the case for all encryption mechanisms or when a secret value, such
as a password, is hashed.
- - the function you use generates a value which can be predicted (pseudo-random).
+ - the function you use is a non-cryptographic PRNG.
- the generated value is used multiple times.
- an attacker can access the generated value.
There is a risk if you answered yes to any of those questions.
Recommended Secure Coding Practices
- - Use functions which rely on a cryptographically strong random number generator such as
random_int()
or random_bytes()
- or openssl_random_pseudo_bytes()
- - When using
openssl_random_pseudo_bytes()
, provide and check the crypto_strong
parameter
+ - Use functions which rely on a cryptographically secure pseudo random number generator (CSPRNG) such as
random_int()
or
+ random_bytes()
or openssl_random_pseudo_bytes()
.
+ - When using
openssl_random_pseudo_bytes()
, provide and check the crypto_strong
parameter.
- Use the generated random values only once.
- You should not expose the generated random value. If you have to store it, make sure that the database or file is secure.
Sensitive Code Example
-$random = rand();
-$random2 = mt_rand(0, 99);
+$random = rand(); // Sensitive
+$random2 = mt_rand(0, 99); // Sensitive
Compliant Solution
-$randomInt = random_int(0,99); // Compliant; generates a cryptographically secure random integer
+$randomInt = random_int(0,99);
See
+ - OWASP - Secure
+ Random Number Generation Cheat Sheet
- OWASP - Top 10 2021 Category A2 - Cryptographic Failures
- OWASP - Top 10 2017 Category A3 - Sensitive Data
Exposure
- - Mobile AppSec Verification Standard - Cryptography Requirements
+ - OWASP - Mobile AppSec Verification Standard - Cryptography Requirements
- OWASP - Mobile Top 10 2016 Category M5 -
Insufficient Cryptography
- CWE - CWE-338 - Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2699.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2699.json
index 537285329..75ff9c139 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2699.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S2699.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "TESTED"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3333.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3333.json
index 7b21f8442..fe96d56e9 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3333.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3333.json
@@ -3,7 +3,7 @@
"type": "VULNERABILITY",
"code": {
"impacts": {
- "SECURITY": "HIGH"
+ "SECURITY": "BLOCKER"
},
"attribute": "CONVENTIONAL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3334.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3334.json
index 312b5eb25..d0dc9182e 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3334.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3334.json
@@ -3,7 +3,7 @@
"type": "VULNERABILITY",
"code": {
"impacts": {
- "SECURITY": "HIGH"
+ "SECURITY": "BLOCKER"
},
"attribute": "COMPLETE"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3336.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3336.json
index 7d78b18fd..901466dc2 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3336.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3336.json
@@ -3,7 +3,7 @@
"type": "VULNERABILITY",
"code": {
"impacts": {
- "SECURITY": "HIGH"
+ "SECURITY": "BLOCKER"
},
"attribute": "COMPLETE"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3337.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3337.json
index 4ddc551bf..1743c6d29 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3337.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3337.json
@@ -3,7 +3,7 @@
"type": "VULNERABILITY",
"code": {
"impacts": {
- "SECURITY": "HIGH"
+ "SECURITY": "BLOCKER"
},
"attribute": "COMPLETE"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3360.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3360.json
index 791f85b87..35bbc3d66 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3360.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S3360.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"code": {
"impacts": {
- "MAINTAINABILITY": "HIGH"
+ "MAINTAINABILITY": "BLOCKER"
},
"attribute": "IDENTIFIABLE"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5632.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5632.json
index 4b0897b6d..53db9110e 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5632.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5632.json
@@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
- "RELIABILITY": "HIGH"
+ "RELIABILITY": "BLOCKER"
},
"attribute": "LOGICAL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5708.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5708.json
index 046c70663..6c78f6f86 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5708.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5708.json
@@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
- "RELIABILITY": "HIGH"
+ "RELIABILITY": "BLOCKER"
},
"attribute": "LOGICAL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5911.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5911.json
index edb0fa7f5..3925671b0 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5911.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S5911.json
@@ -3,7 +3,7 @@
"type": "BUG",
"code": {
"impacts": {
- "RELIABILITY": "HIGH"
+ "RELIABILITY": "BLOCKER"
},
"attribute": "LOGICAL"
},
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.html b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.html
index ece38fff5..455f79a2d 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.html
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.html
@@ -6,9 +6,9 @@
- CVE-2021-42635
Secrets should be stored outside of the source code in a configuration file or a management service for secrets.
-This rule detects variables/fields having a name matching a list of words (secret, token, credential, auth, api[_.-]?key) being assigned a
-pseudorandom hard-coded value. The pseudorandomness of the hard-coded value is based on its entropy and the probability to be human-readable. The
-randomness sensibility can be adjusted if needed. Lower values will detect less random values, raising potentially more false positives.
+This rule detects {detections} having a name matching a list of words (secret, token, credential, auth, api[_.-]?key) being assigned a pseudorandom
+hard-coded value. The pseudorandomness of the hard-coded value is based on its entropy and the probability to be human-readable. The randomness
+sensibility can be adjusted if needed. Lower values will detect less random values, raising potentially more false positives.
Ask Yourself Whether
- The secret allows access to a sensitive component like a database, a file storage, an API, or a service.
diff --git a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.json b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.json
index 41d5aae9f..9c9a735b3 100644
--- a/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.json
+++ b/php-checks/src/main/resources/org/sonar/l10n/php/rules/php/S6418.json
@@ -3,7 +3,7 @@
"type": "SECURITY_HOTSPOT",
"code": {
"impacts": {
- "SECURITY": "HIGH"
+ "SECURITY": "BLOCKER"
},
"attribute": "TRUSTWORTHY"
},
diff --git a/settings.gradle.kts b/settings.gradle.kts
index f19d56e52..618efa2bb 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -35,7 +35,7 @@ include(":sonar-php-plugin")
include(":php-frontend")
include(":php-checks")
include(":php-custom-rules-plugin")
-include(":its:plugin:tests")
+include(":its:plugin")
include(":its:ruling")
gradle.allprojects {
diff --git a/sonarpedia.json b/sonarpedia.json
index f5f00aec2..c9432fd2a 100644
--- a/sonarpedia.json
+++ b/sonarpedia.json
@@ -3,7 +3,7 @@
"languages": [
"PHP"
],
- "latest-update": "2024-09-25T15:28:53.719860Z",
+ "latest-update": "2024-11-13T13:03:49.551706Z",
"options": {
"no-language-in-filenames": true,
"preserve-filenames": true