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