diff options
Diffstat (limited to 'sonar-scanner-engine/src/main')
29 files changed, 368 insertions, 112 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalTempFolderProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalTempFolderProvider.java index 821529b3d60..07711d1b782 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalTempFolderProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalTempFolderProvider.java @@ -31,7 +31,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.impl.utils.DefaultTempFolder; -import org.sonar.api.utils.System2; import org.sonar.api.utils.TempFolder; import org.springframework.context.annotation.Bean; @@ -42,24 +41,13 @@ public class GlobalTempFolderProvider { private static final long CLEAN_MAX_AGE = TimeUnit.DAYS.toMillis(21); static final String TMP_NAME_PREFIX = ".sonartmp_"; - private System2 system; - - public GlobalTempFolderProvider() { - this(new System2()); - } - - GlobalTempFolderProvider(System2 system) { - this.system = system; - } - @Bean("GlobalTempFolder") - public TempFolder provide(ScannerProperties scannerProps) { - - String workingPathName = StringUtils.defaultIfBlank(scannerProps.property(CoreProperties.GLOBAL_WORKING_DIRECTORY), CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE); - Path workingPath = Paths.get(workingPathName); + public TempFolder provide(ScannerProperties scannerProps, SonarUserHome userHome) { + var workingPathName = StringUtils.defaultIfBlank(scannerProps.property(CoreProperties.GLOBAL_WORKING_DIRECTORY), CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE); + var workingPath = Paths.get(workingPathName); if (!workingPath.isAbsolute()) { - Path home = findSonarHome(scannerProps); + var home = userHome.getPath(); workingPath = home.resolve(workingPath).normalize(); } try { @@ -67,7 +55,7 @@ public class GlobalTempFolderProvider { } catch (IOException e) { LOG.error(String.format("failed to clean global working directory: %s", workingPath), e); } - Path tempDir = createTempFolder(workingPath); + var tempDir = createTempFolder(workingPath); return new DefaultTempFolder(tempDir.toFile(), true); } @@ -90,24 +78,8 @@ public class GlobalTempFolderProvider { } } - private Path findSonarHome(ScannerProperties props) { - String home = props.property("sonar.userHome"); - if (home != null) { - return Paths.get(home).toAbsolutePath(); - } - - home = system.envVariable("SONAR_USER_HOME"); - - if (home != null) { - return Paths.get(home).toAbsolutePath(); - } - - home = system.property("user.home"); - return Paths.get(home, ".sonar").toAbsolutePath(); - } - private static void cleanTempFolders(Path path) throws IOException { - if (path.toFile().exists()) { + if (Files.exists(path)) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, new CleanFilter())) { for (Path p : stream) { deleteQuietly(p.toFile()); @@ -118,8 +90,8 @@ public class GlobalTempFolderProvider { private static class CleanFilter implements DirectoryStream.Filter<Path> { @Override - public boolean accept(Path path) throws IOException { - if (!path.toFile().exists()) { + public boolean accept(Path path) { + if (!Files.exists(path)) { return false; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PluginFiles.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PluginFiles.java index 727ada7fbb2..ba53811a9a8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PluginFiles.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PluginFiles.java @@ -26,15 +26,16 @@ import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.nio.file.Files; -import java.util.Objects; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.Optional; -import java.util.stream.Stream; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.config.Configuration; import org.sonar.scanner.bootstrap.ScannerPluginInstaller.InstalledPlugin; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.WsResponse; @@ -51,21 +52,21 @@ public class PluginFiles { private final DefaultScannerWsClient wsClient; private final Configuration configuration; - private final File cacheDir; - private final File tempDir; + private final Path cacheDir; + private final Path tempDir; - public PluginFiles(DefaultScannerWsClient wsClient, Configuration configuration) { + public PluginFiles(DefaultScannerWsClient wsClient, Configuration configuration, SonarUserHome sonarUserHome) { this.wsClient = wsClient; this.configuration = configuration; - File home = locateHomeDir(configuration); - this.cacheDir = mkdir(new File(home, "cache"), "user cache"); - this.tempDir = mkdir(new File(home, "_tmp"), "temp dir"); - LOGGER.info("User cache: {}", cacheDir.getAbsolutePath()); + var home = sonarUserHome.getPath(); + this.cacheDir = mkdir(home.resolve("cache"), "user cache"); + this.tempDir = mkdir(home.resolve("_tmp"), "temp dir"); + LOGGER.info("User cache: {}", cacheDir); } public File createTempDir() { try { - return Files.createTempDirectory(tempDir.toPath(), "plugins").toFile(); + return Files.createTempDirectory(tempDir, "plugins").toFile(); } catch (IOException e) { throw new IllegalStateException("Fail to create temp directory in " + tempDir, e); } @@ -81,24 +82,24 @@ public class PluginFiles { */ public Optional<File> get(InstalledPlugin plugin) { // Does not fail if another process tries to create the directory at the same time. - File jarInCache = jarInCache(plugin.key, plugin.hash); - if (jarInCache.exists() && jarInCache.isFile()) { - return Optional.of(jarInCache); + Path jarInCache = jarInCache(plugin.key, plugin.hash); + if (Files.isRegularFile(jarInCache)) { + return Optional.of(jarInCache.toFile()); } - return download(plugin); + return download(plugin).map(Path::toFile); } - private Optional<File> download(InstalledPlugin plugin) { + private Optional<Path> download(InstalledPlugin plugin) { GetRequest request = new GetRequest("api/plugins/download") .setParam("plugin", plugin.key) .setTimeOutInMs(configuration.getInt(PLUGINS_DOWNLOAD_TIMEOUT_PROPERTY).orElse(PLUGINS_DOWNLOAD_TIMEOUT_DEFAULT) * 1000); - File downloadedFile = newTempFile(); + Path downloadedFile = newTempFile(); LOGGER.debug("Download plugin '{}' to '{}'", plugin.key, downloadedFile); try (WsResponse response = wsClient.call(request)) { Optional<String> expectedMd5 = response.header(MD5_HEADER); - if (!expectedMd5.isPresent()) { + if (expectedMd5.isEmpty()) { throw new IllegalStateException(format( "Fail to download plugin [%s]. Request to %s did not return header %s", plugin.key, response.requestUrl(), MD5_HEADER)); } @@ -114,14 +115,14 @@ public class PluginFiles { // un-compress if needed String cacheMd5; - File tempJar; + Path tempJar; tempJar = downloadedFile; cacheMd5 = expectedMd5.get(); // put in cache - File jarInCache = jarInCache(plugin.key, cacheMd5); - mkdir(jarInCache.getParentFile()); + Path jarInCache = jarInCache(plugin.key, cacheMd5); + mkdir(jarInCache.getParent()); moveFile(tempJar, jarInCache); return Optional.of(jarInCache); @@ -137,82 +138,75 @@ public class PluginFiles { } } - private static void downloadBinaryTo(InstalledPlugin plugin, File downloadedFile, WsResponse response) { + private static void downloadBinaryTo(InstalledPlugin plugin, Path downloadedFile, WsResponse response) { try (InputStream stream = response.contentStream()) { - FileUtils.copyInputStreamToFile(stream, downloadedFile); + FileUtils.copyInputStreamToFile(stream, downloadedFile.toFile()); } catch (IOException e) { throw new IllegalStateException(format("Fail to download plugin [%s] into %s", plugin.key, downloadedFile), e); } } - private File jarInCache(String pluginKey, String hash) { - File hashDir = new File(cacheDir, hash); - File file = new File(hashDir, format("sonar-%s-plugin.jar", pluginKey)); - if (!file.getParentFile().toPath().equals(hashDir.toPath())) { + private Path jarInCache(String pluginKey, String hash) { + Path hashDir = cacheDir.resolve(hash); + Path file = hashDir.resolve(format("sonar-%s-plugin.jar", pluginKey)); + if (!file.getParent().equals(hashDir)) { // vulnerability - attempt to create a file outside the cache directory throw new IllegalStateException(format("Fail to download plugin [%s]. Key is not valid.", pluginKey)); } return file; } - private File newTempFile() { + private Path newTempFile() { try { - return File.createTempFile("fileCache", null, tempDir); + return Files.createTempFile(tempDir, "fileCache", null); } catch (IOException e) { throw new IllegalStateException("Fail to create temp file in " + tempDir, e); } } - private static String computeMd5(File file) { - try (InputStream fis = new BufferedInputStream(FileUtils.openInputStream(file))) { + private static String computeMd5(Path file) { + try (InputStream fis = new BufferedInputStream(Files.newInputStream(file))) { return DigestUtils.md5Hex(fis); } catch (IOException e) { throw new IllegalStateException("Fail to compute md5 of " + file, e); } } - private static void moveFile(File sourceFile, File targetFile) { - boolean rename = sourceFile.renameTo(targetFile); - // Check if the file was cached by another process during download - if (!rename && !targetFile.exists()) { - LOGGER.warn("Unable to rename {} to {}", sourceFile.getAbsolutePath(), targetFile.getAbsolutePath()); - LOGGER.warn("A copy/delete will be tempted but with no guarantee of atomicity"); - try { - Files.move(sourceFile.toPath(), targetFile.toPath()); - } catch (IOException e) { - throw new IllegalStateException("Fail to move " + sourceFile.getAbsolutePath() + " to " + targetFile, e); + private static void moveFile(Path sourceFile, Path targetFile) { + try { + Files.move(sourceFile, targetFile, StandardCopyOption.ATOMIC_MOVE); + } catch (IOException e1) { + // Check if the file was cached by another process during download + if (!Files.exists(targetFile)) { + LOGGER.warn("Unable to rename {} to {}", sourceFile, targetFile); + LOGGER.warn("A copy/delete will be tempted but with no guarantee of atomicity"); + try { + Files.move(sourceFile, targetFile); + } catch (IOException e2) { + throw new IllegalStateException("Fail to move " + sourceFile + " to " + targetFile, e2); + } } } } - private static void mkdir(File dir) { + private static void mkdir(Path dir) { try { - Files.createDirectories(dir.toPath()); + Files.createDirectories(dir); } catch (IOException e) { throw new IllegalStateException("Fail to create cache directory: " + dir, e); } } - private static File mkdir(File dir, String debugTitle) { - if (!dir.isDirectory() || !dir.exists()) { - LOGGER.debug("Create : {}", dir.getAbsolutePath()); + private static Path mkdir(Path dir, String debugTitle) { + if (!Files.isDirectory(dir)) { + LOGGER.debug("Create : {}", dir); try { - Files.createDirectories(dir.toPath()); + Files.createDirectories(dir); } catch (IOException e) { - throw new IllegalStateException("Unable to create " + debugTitle + dir.getAbsolutePath(), e); + throw new IllegalStateException("Unable to create folder " + debugTitle + " at " + dir, e); } } return dir; } - private static File locateHomeDir(Configuration configuration) { - return Stream.of( - configuration.get("sonar.userHome").orElse(null), - System.getenv("SONAR_USER_HOME"), - System.getProperty("user.home") + File.separator + ".sonar") - .filter(Objects::nonNull) - .findFirst() - .map(File::new) - .get(); - } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginInstaller.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginInstaller.java index 9bf367f630d..081410361d6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginInstaller.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginInstaller.java @@ -36,6 +36,7 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.platform.PluginInfo; import org.sonar.core.plugin.PluginType; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonar.scanner.mediumtest.LocalPlugin; import org.sonarqube.ws.client.GetRequest; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHome.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHome.java new file mode 100644 index 00000000000..afbc0bb3c18 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHome.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-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 org.sonar.scanner.bootstrap; + +import java.nio.file.Path; + +public class SonarUserHome { + + private final Path userHome; + + public SonarUserHome(Path userHome) { + this.userHome = userHome; + } + + public Path getPath() { + return userHome; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHomeProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHomeProvider.java new file mode 100644 index 00000000000..da97e011ba8 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHomeProvider.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-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 org.sonar.scanner.bootstrap; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.utils.System2; +import org.springframework.context.annotation.Bean; + +public class SonarUserHomeProvider { + private static final Logger LOG = LoggerFactory.getLogger(SonarUserHomeProvider.class); + + private System2 system; + + public SonarUserHomeProvider() { + this(new System2()); + } + + SonarUserHomeProvider(System2 system) { + this.system = system; + } + + @Bean + public SonarUserHome provide(ScannerProperties scannerProps) { + Path home = findSonarHome(scannerProps); + LOG.debug("Sonar User Home: {}", home); + return new SonarUserHome(home); + } + + private Path findSonarHome(ScannerProperties props) { + var home = props.property("sonar.userHome"); + if (home != null) { + return Paths.get(home).toAbsolutePath(); + } + + home = system.envVariable("SONAR_USER_HOME"); + + if (home != null) { + return Paths.get(home).toAbsolutePath(); + } + + var userHome = Objects.requireNonNull(system.property("user.home"), "The system property 'user.home' is expected to be non null"); + return Paths.get(userHome, ".sonar").toAbsolutePath(); + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java index 69a430dd869..b9de4071df2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java @@ -48,6 +48,7 @@ import org.sonar.core.platform.SpringComponentContainer; import org.sonar.core.util.DefaultHttpDownloader; import org.sonar.core.util.UuidFactoryImpl; import org.sonar.scanner.extension.ScannerCoreExtensionsInstaller; +import org.sonar.scanner.http.ScannerWsClientProvider; import org.sonar.scanner.notifications.DefaultAnalysisWarnings; import org.sonar.scanner.platform.DefaultServer; import org.sonar.scanner.repository.DefaultMetricsRepositoryLoader; @@ -98,6 +99,7 @@ public class SpringGlobalContainer extends SpringComponentContainer { DefaultServer.class, DefaultDocumentationLinkGenerator.class, new GlobalTempFolderProvider(), + new SonarUserHomeProvider(), analysisWarnings, UriReader.class, PluginFiles.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoader.java index f797a2cfdec..33037ede596 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoader.java @@ -30,7 +30,7 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.util.Protobuf; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonar.scanner.protocol.internal.ScannerInternal.SensorCacheEntry; import org.sonar.scanner.protocol.internal.SensorCacheData; import org.sonar.scanner.scan.branch.BranchConfiguration; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/DefaultScannerWsClient.java index 1d192ad9584..a8093c868a7 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/DefaultScannerWsClient.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.bootstrap; +package org.sonar.scanner.http; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -39,6 +39,7 @@ import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.WsClient; import org.sonarqube.ws.client.WsConnector; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClient.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClient.java index 280fb12de95..9b498867016 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClient.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClient.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.bootstrap; +package org.sonar.scanner.http; import org.sonarqube.ws.client.WsRequest; import org.sonarqube.ws.client.WsResponse; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClientProvider.java index 2d8f7328cd4..2511daf5d0f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClientProvider.java @@ -17,16 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.bootstrap; +package org.sonar.scanner.http; import java.net.InetSocketAddress; import java.net.Proxy; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.time.format.DateTimeParseException; +import nl.altindag.ssl.SSLFactory; import org.sonar.api.CoreProperties; import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.utils.System2; import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; +import org.sonar.scanner.bootstrap.ScannerProperties; +import org.sonar.scanner.bootstrap.SonarUserHome; +import org.sonar.scanner.http.ssl.CertificateStore; +import org.sonar.scanner.http.ssl.SslConfig; import org.sonarqube.ws.client.HttpConnector; import org.sonarqube.ws.client.WsClientFactories; import org.springframework.context.annotation.Bean; @@ -52,7 +60,7 @@ public class ScannerWsClientProvider { @Bean("DefaultScannerWsClient") public DefaultScannerWsClient provide(ScannerProperties scannerProps, EnvironmentInformation env, GlobalAnalysisMode globalMode, - System2 system, AnalysisWarnings analysisWarnings) { + System2 system, AnalysisWarnings analysisWarnings, SonarUserHome sonarUserHome) { String url = defaultIfBlank(scannerProps.property("sonar.host.url"), "http://localhost:9000"); HttpConnector.Builder connectorBuilder = HttpConnector.newBuilder().acceptGzip(true); @@ -63,13 +71,16 @@ public class ScannerWsClientProvider { String envVarToken = defaultIfBlank(system.envVariable(TOKEN_ENV_VARIABLE), null); String token = defaultIfBlank(scannerProps.property(TOKEN_PROPERTY), envVarToken); String login = defaultIfBlank(scannerProps.property(CoreProperties.LOGIN), token); + var sslContext = configureSsl(parseSslConfig(scannerProps, sonarUserHome), system); connectorBuilder .readTimeoutMilliseconds(parseDurationProperty(socketTimeout, SONAR_SCANNER_SOCKET_TIMEOUT)) .connectTimeoutMilliseconds(parseDurationProperty(connectTimeout, SONAR_SCANNER_CONNECT_TIMEOUT)) .responseTimeoutMilliseconds(parseDurationProperty(responseTimeout, SONAR_SCANNER_RESPONSE_TIMEOUT)) .userAgent(env.toString()) .url(url) - .credentials(login, scannerProps.property(CoreProperties.PASSWORD)); + .credentials(login, scannerProps.property(CoreProperties.PASSWORD)) + .setSSLSocketFactory(sslContext.getSslSocketFactory()) + .setTrustManager(sslContext.getTrustManager().orElseThrow()); // OkHttp detects 'http.proxyHost' java property already, so just focus on sonar properties String proxyHost = defaultIfBlank(scannerProps.property("sonar.scanner.proxyHost"), null); @@ -109,4 +120,33 @@ public class ScannerWsClientProvider { return parseIntProperty(propValue, propKey) * 1_000; } } + + private static SslConfig parseSslConfig(ScannerProperties scannerProperties, SonarUserHome sonarUserHome) { + var keyStorePath = defaultIfBlank(scannerProperties.property("sonar.scanner.keystorePath"), sonarUserHome.getPath().resolve("ssl/keystore.p12").toString()); + var keyStorePassword = defaultIfBlank(scannerProperties.property("sonar.scanner.keystorePassword"), CertificateStore.DEFAULT_PASSWORD); + var trustStorePath = defaultIfBlank(scannerProperties.property("sonar.scanner.truststorePath"), sonarUserHome.getPath().resolve("ssl/truststore.p12").toString()); + var trustStorePassword = defaultIfBlank(scannerProperties.property("sonar.scanner.truststorePassword"), CertificateStore.DEFAULT_PASSWORD); + var keyStore = new CertificateStore(Path.of(keyStorePath), keyStorePassword); + var trustStore = new CertificateStore(Path.of(trustStorePath), trustStorePassword); + return new SslConfig(keyStore, trustStore); + } + + private static SSLFactory configureSsl(SslConfig sslConfig, System2 system2) { + var sslFactoryBuilder = SSLFactory.builder() + .withDefaultTrustMaterial() + .withSystemTrustMaterial(); + if (system2.properties().containsKey("javax.net.ssl.keyStore")) { + sslFactoryBuilder.withSystemPropertyDerivedIdentityMaterial(); + } + var keyStore = sslConfig.getKeyStore(); + if (keyStore != null && Files.exists(keyStore.getPath())) { + sslFactoryBuilder.withIdentityMaterial(keyStore.getPath(), keyStore.getKeyStorePassword().toCharArray(), keyStore.getKeyStoreType()); + } + var trustStore = sslConfig.getTrustStore(); + if (trustStore != null && Files.exists(trustStore.getPath())) { + sslFactoryBuilder.withTrustMaterial(trustStore.getPath(), trustStore.getKeyStorePassword().toCharArray(), trustStore.getKeyStoreType()); + } + return sslFactoryBuilder.build(); + } + } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/package-info.java new file mode 100644 index 00000000000..349a6066fcb --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-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. + */ +@ParametersAreNonnullByDefault +package org.sonar.scanner.http; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/CertificateStore.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/CertificateStore.java new file mode 100644 index 00000000000..b285d3bb478 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/CertificateStore.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-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 org.sonar.scanner.http.ssl; + +import java.nio.file.Path; + +public class CertificateStore { + public static final String DEFAULT_PASSWORD = "sonar"; + public static final String DEFAULT_STORE_TYPE = "PKCS12"; + private final Path path; + private final String keyStorePassword; + private final String keyStoreType; + + public CertificateStore(Path path, String keyStorePassword) { + this.path = path; + this.keyStorePassword = keyStorePassword; + this.keyStoreType = DEFAULT_STORE_TYPE; + } + + public Path getPath() { + return path; + } + + public String getKeyStorePassword() { + return keyStorePassword; + } + + public String getKeyStoreType() { + return keyStoreType; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/SslConfig.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/SslConfig.java new file mode 100644 index 00000000000..8f172ca2534 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/SslConfig.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-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 org.sonar.scanner.http.ssl; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class SslConfig { + private final CertificateStore keyStore; + private final CertificateStore trustStore; + + public SslConfig(@Nullable CertificateStore keyStore, @Nullable CertificateStore trustStore) { + this.keyStore = keyStore; + this.trustStore = trustStore; + } + + @CheckForNull + public CertificateStore getKeyStore() { + return keyStore; + } + + @CheckForNull + public CertificateStore getTrustStore() { + return trustStore; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/package-info.java new file mode 100644 index 00000000000..e1e4c18147f --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-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. + */ +@ParametersAreNonnullByDefault +package org.sonar.scanner.http.ssl; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/platform/DefaultServer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/platform/DefaultServer.java index 9ec8ac9ae9c..a53f3de7f85 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/platform/DefaultServer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/platform/DefaultServer.java @@ -26,7 +26,7 @@ import org.sonar.api.config.Configuration; import org.sonar.api.platform.Server; import org.sonar.api.utils.DateUtils; import org.sonar.core.platform.SonarQubeVersion; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import static org.apache.commons.lang3.StringUtils.trimToEmpty; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java index 7dce5a4bee5..aa4c30d352b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java @@ -27,7 +27,7 @@ import org.sonar.api.Startable; import org.sonar.api.utils.MessageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.report.CeTaskReportDataHolder; import org.sonar.scanner.scan.ScanProperties; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java index 2f1787885ad..15f12eef49d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java @@ -43,7 +43,7 @@ import org.sonar.api.utils.MessageException; import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.ZipUtils; import org.sonar.core.ce.CeTaskCharacteristics; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.ci.CiConfiguration; import org.sonar.scanner.fs.InputModuleHierarchy; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoader.java index a69c13e2b0c..df65d94fa98 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoader.java @@ -25,7 +25,7 @@ import java.util.ArrayList; import java.util.List; import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric.ValueType; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonar.scanner.protocol.GsonHelper; import org.sonarqube.ws.client.GetRequest; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java index 2a6b78be421..6403b5fcdaa 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java @@ -21,7 +21,7 @@ package org.sonar.scanner.repository; import java.io.IOException; import java.io.InputStream; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.NewCodePeriods; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.HttpException; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java index a35b66568e1..b6c681cdb12 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java @@ -28,7 +28,7 @@ import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.impl.utils.ScannerUtils; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.Batch.WsProjectResponse; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.HttpException; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java index 30c716fc05a..cc5c2caa264 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java @@ -28,7 +28,7 @@ import java.util.Map; import java.util.function.BinaryOperator; import java.util.function.Supplier; import org.sonar.api.utils.MessageException; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.Qualityprofiles.SearchWsResponse; import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.client.GetRequest; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java index 9b004f39e8b..58aeb9695f8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java @@ -28,7 +28,7 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.config.Configuration; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.client.GetRequest; public class DefaultLanguagesLoader implements LanguagesLoader { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/AbstractSettingsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/AbstractSettingsLoader.java index 6df3e293910..61b68ce9155 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/AbstractSettingsLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/AbstractSettingsLoader.java @@ -34,7 +34,7 @@ import org.sonar.api.impl.utils.ScannerUtils; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonarqube.ws.Settings; import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.HttpException; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoader.java index e5fabcfe48e..04d1feb1e95 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoader.java @@ -21,7 +21,7 @@ package org.sonar.scanner.repository.settings; import java.util.Map; import javax.inject.Inject; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; public class DefaultGlobalSettingsLoader extends AbstractSettingsLoader implements GlobalSettingsLoader { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoader.java index e03e92d82e0..61b0d669248 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoader.java @@ -20,7 +20,7 @@ package org.sonar.scanner.repository.settings; import java.util.Map; -import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.http.DefaultScannerWsClient; import org.sonar.scanner.bootstrap.ScannerProperties; public class DefaultProjectSettingsLoader extends AbstractSettingsLoader implements ProjectSettingsLoader { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java index 242ace13d31..1bc34c0198c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java @@ -31,7 +31,7 @@ import org.sonar.api.batch.rule.LoadedActiveRule; import org.sonar.api.impl.utils.ScannerUtils; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; -import org.sonar.scanner.bootstrap.ScannerWsClient; +import org.sonar.scanner.http.ScannerWsClient; import org.sonarqube.ws.Common.Paging; import org.sonarqube.ws.Rules; import org.sonarqube.ws.Rules.Active; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DeprecatedPropertiesWarningGenerator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DeprecatedPropertiesWarningGenerator.java index 244fbe4aea9..2022782148c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DeprecatedPropertiesWarningGenerator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DeprecatedPropertiesWarningGenerator.java @@ -28,7 +28,7 @@ import org.sonar.api.CoreProperties; import org.sonar.api.config.Configuration; import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.batch.bootstrapper.EnvironmentInformation; -import org.sonar.scanner.bootstrap.ScannerWsClientProvider; +import org.sonar.scanner.http.ScannerWsClientProvider; public class DeprecatedPropertiesWarningGenerator { private static final Logger LOG = LoggerFactory.getLogger(DeprecatedPropertiesWarningGenerator.class); diff --git a/sonar-scanner-engine/src/main/resources/logback.xml b/sonar-scanner-engine/src/main/resources/logback.xml index c64f28abec7..ccd0dfe09b9 100644 --- a/sonar-scanner-engine/src/main/resources/logback.xml +++ b/sonar-scanner-engine/src/main/resources/logback.xml @@ -26,4 +26,9 @@ <logger name="org.springframework.context.annotation.AnnotationConfigApplicationContext" level="ERROR"/> <logger name="org.sonar.core.platform.PriorityBeanFactory" level="INFO"/> + + <!-- CertificateUtils is too verbose when loading system certificates --> + <logger name="nl.altindag.ssl.util.CertificateUtils" level="INFO"/> + + </configuration>
\ No newline at end of file diff --git a/sonar-scanner-engine/src/main/resources/org/sonar/batch/bootstrapper/logback.xml b/sonar-scanner-engine/src/main/resources/org/sonar/batch/bootstrapper/logback.xml index ccf804462b6..37d6c262d1d 100644 --- a/sonar-scanner-engine/src/main/resources/org/sonar/batch/bootstrapper/logback.xml +++ b/sonar-scanner-engine/src/main/resources/org/sonar/batch/bootstrapper/logback.xml @@ -36,4 +36,7 @@ <logger name="org.sonar.core.platform.PriorityBeanFactory" level="INFO"/> + <!-- CertificateUtils is too verbose when loading system certificates --> + <logger name="nl.altindag.ssl.util.CertificateUtils" level="INFO"/> + </configuration> |