Explorar el Código

SONAR-22038 Support new SSL properties (#10987)

* Move scanner HttpClient code in its own package
* Factorize the computation of the Sonar User Home
master
Julien HENRY hace 3 semanas
padre
commit
ce849ff5f2
Se han modificado 69 ficheros con 1935 adiciones y 533 borrados
  1. 2
    0
      build.gradle
  2. 2
    0
      sonar-scanner-engine/build.gradle
  3. 8
    36
      sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalTempFolderProvider.java
  4. 51
    57
      sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PluginFiles.java
  5. 1
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginInstaller.java
  6. 35
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHome.java
  7. 66
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHomeProvider.java
  8. 2
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java
  9. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoader.java
  10. 2
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/DefaultScannerWsClient.java
  11. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClient.java
  12. 43
    3
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClientProvider.java
  13. 23
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/package-info.java
  14. 48
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/CertificateStore.java
  15. 43
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/SslConfig.java
  16. 23
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/package-info.java
  17. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/platform/DefaultServer.java
  18. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java
  19. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
  20. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoader.java
  21. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java
  22. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java
  23. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java
  24. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java
  25. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/AbstractSettingsLoader.java
  26. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoader.java
  27. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoader.java
  28. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java
  29. 1
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DeprecatedPropertiesWarningGenerator.java
  30. 5
    0
      sonar-scanner-engine/src/main/resources/logback.xml
  31. 3
    0
      sonar-scanner-engine/src/main/resources/org/sonar/batch/bootstrapper/logback.xml
  32. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java
  33. 48
    80
      sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/GlobalTempFolderProviderTest.java
  34. 96
    76
      sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java
  35. 1
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginInstallerTest.java
  36. 0
    251
      sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
  37. 67
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/SonarUserHomeProviderTest.java
  38. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoaderTest.java
  39. 3
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/http/DefaultScannerWsClientTest.java
  40. 413
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/http/ScannerWsClientProviderTest.java
  41. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/platform/DefaultServerTest.java
  42. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/qualitygate/QualityGateCheckTest.java
  43. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java
  44. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoaderTest.java
  45. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoaderTest.java
  46. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoaderTest.java
  47. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultQualityProfileLoaderTest.java
  48. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/language/DefaultLanguagesRepositoryTest.java
  49. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoaderTest.java
  50. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoaderTest.java
  51. 1
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java
  52. 452
    0
      sonar-scanner-engine/src/test/resources/ssl/README.md
  53. 33
    0
      sonar-scanner-engine/src/test/resources/ssl/ca-client-auth.crt
  54. 52
    0
      sonar-scanner-engine/src/test/resources/ssl/ca-client-auth.key
  55. 33
    0
      sonar-scanner-engine/src/test/resources/ssl/ca.crt
  56. 52
    0
      sonar-scanner-engine/src/test/resources/ssl/ca.key
  57. BIN
      sonar-scanner-engine/src/test/resources/ssl/client-truststore.p12
  58. 29
    0
      sonar-scanner-engine/src/test/resources/ssl/client.csr
  59. 52
    0
      sonar-scanner-engine/src/test/resources/ssl/client.key
  60. BIN
      sonar-scanner-engine/src/test/resources/ssl/client.p12
  61. 31
    0
      sonar-scanner-engine/src/test/resources/ssl/client.pem
  62. 33
    0
      sonar-scanner-engine/src/test/resources/ssl/openssl-client-auth.conf
  63. 34
    0
      sonar-scanner-engine/src/test/resources/ssl/openssl.conf
  64. BIN
      sonar-scanner-engine/src/test/resources/ssl/server-with-client-ca.p12
  65. 29
    0
      sonar-scanner-engine/src/test/resources/ssl/server.csr
  66. 52
    0
      sonar-scanner-engine/src/test/resources/ssl/server.key
  67. BIN
      sonar-scanner-engine/src/test/resources/ssl/server.p12
  68. 33
    0
      sonar-scanner-engine/src/test/resources/ssl/server.pem
  69. 7
    0
      sonar-scanner-engine/src/test/resources/ssl/v3.ext

+ 2
- 0
build.gradle Ver fichero

@@ -315,6 +315,7 @@ subprojects {
exclude 'junit:junit'
}
dependency 'com.squareup.okio:okio:3.7.0'
dependency 'io.github.hakky54:sslcontext-kickstart:8.3.4'
dependency 'io.prometheus:simpleclient:0.16.0'
dependency 'io.prometheus:simpleclient_common:0.16.0'
dependency 'io.prometheus:simpleclient_servlet:0.16.0'
@@ -370,6 +371,7 @@ subprojects {
entry 'junit-jupiter-params'
entry 'junit-vintage-engine'
}
dependency 'org.junit-pioneer:junit-pioneer:2.2.0'
dependency 'org.xmlunit:xmlunit-core:2.9.1'
dependency 'org.xmlunit:xmlunit-matchers:2.9.1'
dependency 'org.lz4:lz4-java:1.8.0'

+ 2
- 0
sonar-scanner-engine/build.gradle Ver fichero

@@ -27,6 +27,7 @@ dependencies {
api 'com.google.protobuf:protobuf-java'
api 'com.squareup.okhttp3:okhttp'
api 'com.fasterxml.staxmate:staxmate'
implementation 'io.github.hakky54:sslcontext-kickstart'
api 'javax.annotation:javax.annotation-api'
api 'org.eclipse.jgit:org.eclipse.jgit'
api 'org.tmatesoft.svnkit:svnkit'
@@ -64,6 +65,7 @@ dependencies {
testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures'
testImplementation project(':plugins:sonar-xoo-plugin')
testImplementation 'org.wiremock:wiremock-standalone'
testImplementation 'org.junit-pioneer:junit-pioneer'

testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'

+ 8
- 36
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalTempFolderProvider.java Ver fichero

@@ -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;
}


+ 51
- 57
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/PluginFiles.java Ver fichero

@@ -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();
}
}

+ 1
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerPluginInstaller.java Ver fichero

@@ -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;


+ 35
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHome.java Ver fichero

@@ -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;
}
}

+ 66
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SonarUserHomeProvider.java Ver fichero

@@ -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();
}

}

+ 2
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/SpringGlobalContainer.java Ver fichero

@@ -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,

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoader.java Ver fichero

@@ -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;

sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java → sonar-scanner-engine/src/main/java/org/sonar/scanner/http/DefaultScannerWsClient.java Ver fichero

@@ -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;

sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClient.java → sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClient.java Ver fichero

@@ -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;

sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java → sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ScannerWsClientProvider.java Ver fichero

@@ -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();
}

}

+ 23
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/http/package-info.java Ver fichero

@@ -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;

+ 48
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/CertificateStore.java Ver fichero

@@ -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;
}
}

+ 43
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/SslConfig.java Ver fichero

@@ -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;
}
}

+ 23
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/http/ssl/package-info.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/platform/DefaultServer.java Ver fichero

@@ -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;


+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoader.java Ver fichero

@@ -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;


+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoader.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoader.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/DefaultQualityProfileLoader.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java Ver fichero

@@ -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 {

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/AbstractSettingsLoader.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoader.java Ver fichero

@@ -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 {


+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoader.java Ver fichero

@@ -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 {

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java Ver fichero

@@ -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;

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DeprecatedPropertiesWarningGenerator.java Ver fichero

@@ -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);

+ 5
- 0
sonar-scanner-engine/src/main/resources/logback.xml Ver fichero

@@ -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>

+ 3
- 0
sonar-scanner-engine/src/main/resources/org/sonar/batch/bootstrapper/logback.xml Ver fichero

@@ -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>

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/WsTestUtil.java Ver fichero

@@ -24,7 +24,7 @@ import java.io.Reader;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.mockito.ArgumentMatcher;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonarqube.ws.client.WsRequest;
import org.sonarqube.ws.client.WsResponse;


+ 48
- 80
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/GlobalTempFolderProviderTest.java Ver fichero

@@ -23,139 +23,107 @@ import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileTime;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.sonar.api.CoreProperties;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.TempFolder;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class GlobalTempFolderProviderTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
class GlobalTempFolderProviderTest {

private GlobalTempFolderProvider tempFolderProvider = new GlobalTempFolderProvider();
private final SonarUserHome sonarUserHome = mock(SonarUserHome.class);
private final GlobalTempFolderProvider underTest = new GlobalTempFolderProvider();

@Test
public void createTempFolderProps() throws Exception {
File workingDir = temp.newFolder();
workingDir.delete();
void createTempFolderProps(@TempDir Path workingDir) throws Exception {
Files.delete(workingDir);

TempFolder tempFolder = tempFolderProvider.provide(
new ScannerProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath())));
var tempFolder = underTest.provide(
new ScannerProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.toAbsolutePath().toString())), sonarUserHome);
tempFolder.newDir();
tempFolder.newFile();
assertThat(getCreatedTempDir(workingDir)).exists();
assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);

FileUtils.deleteQuietly(workingDir);
assertThat(workingDir).isDirectory();
assertThat(workingDir.toFile().list()).hasSize(1);
var rootTmpDir = workingDir.toFile().listFiles()[0];
assertThat(rootTmpDir.list()).hasSize(2);
}

@Test
public void cleanUpOld() throws IOException {
void cleanUpOld(@TempDir Path workingDir) throws IOException {
long creationTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(100);
File workingDir = temp.newFolder();

for (int i = 0; i < 3; i++) {
File tmp = new File(workingDir, ".sonartmp_" + i);
tmp.mkdirs();
Path tmp = workingDir.resolve(".sonartmp_" + i);
Files.createDirectories(tmp);
setFileCreationDate(tmp, creationTime);
}

tempFolderProvider.provide(
new ScannerProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.getAbsolutePath())));
// this also checks that all other temps were deleted
assertThat(getCreatedTempDir(workingDir)).exists();
underTest.provide(
new ScannerProperties(ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, workingDir.toAbsolutePath().toString())), sonarUserHome);

FileUtils.deleteQuietly(workingDir);
// this also checks that all other temps were deleted
assertThat(workingDir.toFile().list()).hasSize(1);
}

@Test
public void createTempFolderSonarHome() throws Exception {
void createTempFolderFromSonarHome(@TempDir Path sonarUserHomePath) throws Exception {
// with sonar home, it will be in {sonar.home}/.sonartmp
File sonarHome = temp.newFolder();
File workingDir = new File(sonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile();
when(sonarUserHome.getPath()).thenReturn(sonarUserHomePath);
var expectedWorkingDir = sonarUserHomePath.resolve(CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE);

TempFolder tempFolder = tempFolderProvider.provide(
new ScannerProperties(ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath())));
TempFolder tempFolder = underTest.provide(new ScannerProperties(Map.of()), sonarUserHome);
tempFolder.newDir();
tempFolder.newFile();
assertThat(getCreatedTempDir(workingDir)).exists();
assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);

FileUtils.deleteQuietly(sonarHome);
assertThat(expectedWorkingDir).isDirectory();
assertThat(expectedWorkingDir.toFile().list()).hasSize(1);
var rootTmpDir = expectedWorkingDir.toFile().listFiles()[0];
assertThat(rootTmpDir.list()).hasSize(2);
}

@Test
public void createTempFolderDefault() throws Exception {
System2 system = mock(System2.class);
tempFolderProvider = new GlobalTempFolderProvider(system);
File userHome = temp.newFolder();

when(system.envVariable("SONAR_USER_HOME")).thenReturn(null);
when(system.property("user.home")).thenReturn(userHome.getAbsolutePath());

// if nothing is defined, it will be in {user.home}/.sonar/.sonartmp
File defaultSonarHome = new File(userHome.getAbsolutePath(), ".sonar");
File workingDir = new File(defaultSonarHome, CoreProperties.GLOBAL_WORKING_DIRECTORY_DEFAULT_VALUE).getAbsoluteFile();
try {
TempFolder tempFolder = tempFolderProvider.provide(
new ScannerProperties(Collections.emptyMap()));
tempFolder.newDir();
tempFolder.newFile();
assertThat(getCreatedTempDir(workingDir)).exists();
assertThat(getCreatedTempDir(workingDir).list()).hasSize(2);
} finally {
FileUtils.deleteQuietly(workingDir);
}
}

@Test
public void dotWorkingDir() {
File sonarHome = temp.getRoot();
void dotWorkingDir(@TempDir Path sonarUserHomePath) {
when(sonarUserHome.getPath()).thenReturn(sonarUserHomePath);
String globalWorkDir = ".";
ScannerProperties globalProperties = new ScannerProperties(
ImmutableMap.of("sonar.userHome", sonarHome.getAbsolutePath(), CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkDir));
ImmutableMap.of(CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkDir));

TempFolder tempFolder = tempFolderProvider.provide(globalProperties);
var tempFolder = underTest.provide(globalProperties, sonarUserHome);
File newFile = tempFolder.newFile();
assertThat(newFile.getParentFile().getParentFile().getAbsolutePath()).isEqualTo(sonarHome.getAbsolutePath());
assertThat(newFile.getParentFile().getParentFile().toPath()).isEqualTo(sonarUserHomePath);
assertThat(newFile.getParentFile().getName()).startsWith(".sonartmp_");
}

@Test
public void homeIsSymbolicLink() throws IOException {
void homeIsSymbolicLink(@TempDir Path realSonarHome, @TempDir Path symlink) throws IOException {
assumeTrue(!System2.INSTANCE.isOsWindows());
File realSonarHome = temp.newFolder();
File symlink = temp.newFolder();
symlink.delete();
Files.createSymbolicLink(symlink.toPath(), realSonarHome.toPath());
ScannerProperties globalProperties = new ScannerProperties(ImmutableMap.of("sonar.userHome", symlink.getAbsolutePath()));
symlink.toFile().delete();
Files.createSymbolicLink(symlink, realSonarHome);
when(sonarUserHome.getPath()).thenReturn(symlink);

ScannerProperties globalProperties = new ScannerProperties(Map.of());

TempFolder tempFolder = tempFolderProvider.provide(globalProperties);
TempFolder tempFolder = underTest.provide(globalProperties, sonarUserHome);
File newFile = tempFolder.newFile();
assertThat(newFile.getParentFile().getParentFile().getAbsolutePath()).isEqualTo(symlink.getAbsolutePath());
assertThat(newFile.getParentFile().getParentFile().toPath().toAbsolutePath()).isEqualTo(symlink);
assertThat(newFile.getParentFile().getName()).startsWith(".sonartmp_");
}

private File getCreatedTempDir(File workingDir) {
assertThat(workingDir).isDirectory();
assertThat(workingDir.listFiles()).hasSize(1);
return workingDir.listFiles()[0];
}

private void setFileCreationDate(File f, long time) throws IOException {
BasicFileAttributeView attributes = Files.getFileAttributeView(f.toPath(), BasicFileAttributeView.class);
private void setFileCreationDate(Path f, long time) throws IOException {
BasicFileAttributeView attributes = Files.getFileAttributeView(f, BasicFileAttributeView.class);
FileTime creationTime = FileTime.fromMillis(time);
attributes.setTimes(creationTime, creationTime, creationTime);
}

+ 96
- 76
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java Ver fichero

@@ -19,98 +19,113 @@
*/
package org.sonar.scanner.bootstrap;

import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okio.Buffer;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.scanner.bootstrap.ScannerPluginInstaller.InstalledPlugin;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonarqube.ws.client.HttpConnector;
import org.sonarqube.ws.client.WsClientFactories;

import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
import static com.github.tomakehurst.wiremock.client.WireMock.exactly;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.notFound;
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
import static com.github.tomakehurst.wiremock.client.WireMock.serverError;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.apache.commons.io.FileUtils.moveFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.scanner.bootstrap.PluginFiles.PLUGINS_DOWNLOAD_TIMEOUT_PROPERTY;

public class PluginFilesTest {
class PluginFilesTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Rule
public MockWebServer server = new MockWebServer();
@RegisterExtension
static WireMockExtension sonarqube = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

@TempDir
private Path tempDir;

private final SonarUserHome sonarUserHome = mock(SonarUserHome.class);

private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class);

private File userHome;
private PluginFiles underTest;

@Before
public void setUp() throws Exception {
HttpConnector connector = HttpConnector.newBuilder().acceptGzip(true).url(server.url("/").toString()).build();
@BeforeEach
void setUp(@TempDir Path sonarUserHomeDir) throws Exception {
when(sonarUserHome.getPath()).thenReturn(sonarUserHomeDir);

HttpConnector connector = HttpConnector.newBuilder().acceptGzip(true).url(sonarqube.url("/")).build();
GlobalAnalysisMode analysisMode = new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()));
DefaultScannerWsClient wsClient = new DefaultScannerWsClient(WsClientFactories.getDefault().newClient(connector), false,
analysisMode, analysisWarnings);

userHome = temp.newFolder();
MapSettings settings = new MapSettings();
settings.setProperty("sonar.userHome", userHome.getAbsolutePath());
settings.setProperty(PLUGINS_DOWNLOAD_TIMEOUT_PROPERTY, 1);

underTest = new PluginFiles(wsClient, settings.asConfig());
underTest = new PluginFiles(wsClient, settings.asConfig(), sonarUserHome);
}

@Test
public void get_jar_from_cache_if_present() throws Exception {
void get_jar_from_cache_if_present() throws Exception {
FileAndMd5 jar = createFileInCache("foo");

File result = underTest.get(newInstalledPlugin("foo", jar.md5)).get();

verifySameContent(result, jar);
verifySameContent(result.toPath(), jar);
// no requests to server
assertThat(server.getRequestCount()).isZero();
sonarqube.verify(0, anyRequestedFor(anyUrl()));
}

@Test
public void download_and_add_jar_to_cache_if_missing() throws Exception {
void download_and_add_jar_to_cache_if_missing() throws Exception {
FileAndMd5 tempJar = new FileAndMd5();
enqueueDownload(tempJar);
stubDownload(tempJar);

InstalledPlugin plugin = newInstalledPlugin("foo", tempJar.md5);
File result = underTest.get(plugin).get();

verifySameContent(result, tempJar);
HttpUrl requestedUrl = server.takeRequest().getRequestUrl();
assertThat(requestedUrl.encodedPath()).isEqualTo("/api/plugins/download");
assertThat(requestedUrl.encodedQuery()).isEqualTo("plugin=foo");
verifySameContent(result.toPath(), tempJar);

sonarqube.verify(exactly(1), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo")));

// get from cache on second call
result = underTest.get(plugin).get();
verifySameContent(result, tempJar);
assertThat(server.getRequestCount()).isOne();
verifySameContent(result.toPath(), tempJar);

sonarqube.verify(exactly(1), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo")));
}

@Test
public void return_empty_if_plugin_not_found_on_server() {
server.enqueue(new MockResponse().setResponseCode(404));
void return_empty_if_plugin_not_found_on_server() {
sonarqube.stubFor(get(anyUrl())
.willReturn(notFound()));

InstalledPlugin plugin = newInstalledPlugin("foo", "abc");
Optional<File> result = underTest.get(plugin);
@@ -119,9 +134,9 @@ public class PluginFilesTest {
}

@Test
public void fail_if_integrity_of_download_is_not_valid() throws IOException {
void fail_if_integrity_of_download_is_not_valid() throws IOException {
FileAndMd5 tempJar = new FileAndMd5();
enqueueDownload(tempJar.file, "invalid_hash");
stubDownload(tempJar.file, "invalid_hash");
InstalledPlugin plugin = newInstalledPlugin("foo", "abc");

expectISE("foo", "was expected to have checksum invalid_hash but had " + tempJar.md5,
@@ -129,63 +144,68 @@ public class PluginFilesTest {
}

@Test
public void fail_if_md5_header_is_missing_from_response() throws IOException {
File tempJar = temp.newFile();
enqueueDownload(tempJar, null);
void fail_if_md5_header_is_missing_from_response(@TempDir Path tempDir) throws IOException {
var tempJar = Files.createTempFile(tempDir, "plugin", ".jar");
stubDownload(tempJar, null);
InstalledPlugin plugin = newInstalledPlugin("foo", "abc");

expectISE("foo", "did not return header Sonar-MD5", () -> underTest.get(plugin));
}

@Test
public void fail_if_server_returns_error() {
server.enqueue(new MockResponse().setResponseCode(500));
void fail_if_server_returns_error() {
sonarqube.stubFor(get(anyUrl())
.willReturn(serverError()));

InstalledPlugin plugin = newInstalledPlugin("foo", "abc");

expectISE("foo", "returned code 500", () -> underTest.get(plugin));
}

@Test
public void getPlugin_whenTimeOutReached_thenDownloadFails() {
MockResponse response = new MockResponse().setBody("test").setBodyDelay(2, TimeUnit.SECONDS);
response.setHeader("Sonar-MD5", "md5");
server.enqueue(response);
void getPlugin_whenTimeOutReached_thenDownloadFails() {
sonarqube.stubFor(get(anyUrl())
.willReturn(ok()
.withFixedDelay(5000)));

InstalledPlugin plugin = newInstalledPlugin("foo", "abc");

assertThatThrownBy(() -> underTest.get(plugin))
.isInstanceOf(IllegalStateException.class)
.hasMessageStartingWith("Fail to download plugin [" + plugin.key + "]")
.hasMessageStartingWith("Fail to request url")
.cause().isInstanceOf(SocketTimeoutException.class);
}

@Test
public void download_a_new_version_of_plugin_during_blue_green_switch() throws IOException {
void download_a_new_version_of_plugin_during_blue_green_switch() throws IOException {
FileAndMd5 tempJar = new FileAndMd5();
enqueueDownload(tempJar);
stubDownload(tempJar);

// expecting to download plugin foo with checksum "abc"
InstalledPlugin pluginV1 = newInstalledPlugin("foo", "abc");

File result = underTest.get(pluginV1).get();
verifySameContent(result, tempJar);
verifySameContent(result.toPath(), tempJar);

// new version of downloaded jar is put in cache with the new md5
InstalledPlugin pluginV2 = newInstalledPlugin("foo", tempJar.md5);
result = underTest.get(pluginV2).get();
verifySameContent(result, tempJar);
assertThat(server.getRequestCount()).isOne();
verifySameContent(result.toPath(), tempJar);

sonarqube.verify(exactly(1), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo")));

// v1 still requests server and downloads v2
enqueueDownload(tempJar);
stubDownload(tempJar);
result = underTest.get(pluginV1).get();
verifySameContent(result, tempJar);
assertThat(server.getRequestCount()).isEqualTo(2);
verifySameContent(result.toPath(), tempJar);

sonarqube.verify(exactly(2), getRequestedFor(urlEqualTo("/api/plugins/download?plugin=foo")));
}

@Test
public void fail_if_cached_file_is_outside_cache_dir() throws IOException {
void fail_if_cached_file_is_outside_cache_dir() throws IOException {
FileAndMd5 tempJar = new FileAndMd5();
enqueueDownload(tempJar);
stubDownload(tempJar);

InstalledPlugin plugin = newInstalledPlugin("foo/bar", "abc");

@@ -200,29 +220,29 @@ public class PluginFilesTest {
}

private FileAndMd5 moveToCache(String pluginKey, FileAndMd5 jar) throws IOException {
File jarInCache = new File(userHome, "cache/" + jar.md5 + "/sonar-" + pluginKey + "-plugin.jar");
moveFile(jar.file, jarInCache);
Path jarInCache = sonarUserHome.getPath().resolve("cache/" + jar.md5 + "/sonar-" + pluginKey + "-plugin.jar");
moveFile(jar.file.toFile(), jarInCache.toFile());
return new FileAndMd5(jarInCache, jar.md5);
}

/**
* Enqueue download of file with valid MD5
*/
private void enqueueDownload(FileAndMd5 file) throws IOException {
enqueueDownload(file.file, file.md5);
private void stubDownload(FileAndMd5 file) throws IOException {
stubDownload(file.file, file.md5);
}

/**
* Enqueue download of file with a MD5 that may not be returned (null) or not valid
*/
private void enqueueDownload(File file, @Nullable String md5) throws IOException {
Buffer body = new Buffer();
body.write(FileUtils.readFileToByteArray(file));
MockResponse response = new MockResponse().setBody(body);
private void stubDownload(Path file, @Nullable String md5) throws IOException {
var responseDefBuilder = ok();
if (md5 != null) {
response.setHeader("Sonar-MD5", md5);
responseDefBuilder.withHeader("Sonar-MD5", md5);
}
server.enqueue(response);
responseDefBuilder.withBody(Files.readAllBytes(file));
sonarqube.stubFor(get(urlMatching("/api/plugins/download\\?plugin=.*"))
.willReturn(responseDefBuilder));
}

private static InstalledPlugin newInstalledPlugin(String pluginKey, String fileChecksum) {
@@ -232,10 +252,10 @@ public class PluginFilesTest {
return plugin;
}

private static void verifySameContent(File file1, FileAndMd5 file2) {
assertThat(file1).isFile().exists();
assertThat(file2.file).isFile().exists();
assertThat(file1).hasSameContentAs(file2.file);
private static void verifySameContent(Path file1, FileAndMd5 file2) {
assertThat(file1).isRegularFile();
assertThat(file2.file).isRegularFile();
assertThat(file1).hasSameTextualContentAs(file2.file);
}

private void expectISE(String pluginKey, String message, ThrowingCallable shouldRaiseThrowable) {
@@ -246,18 +266,18 @@ public class PluginFilesTest {
}

private class FileAndMd5 {
private final File file;
private final Path file;
private final String md5;

FileAndMd5(File file, String md5) {
FileAndMd5(Path file, String md5) {
this.file = file;
this.md5 = md5;
}

FileAndMd5() throws IOException {
this.file = temp.newFile();
FileUtils.write(this.file, RandomStringUtils.random(3));
try (InputStream fis = FileUtils.openInputStream(this.file)) {
this.file = Files.createTempFile(tempDir, "jar", null);
Files.write(this.file, RandomStringUtils.random(3).getBytes());
try (InputStream fis = Files.newInputStream(this.file)) {
this.md5 = DigestUtils.md5Hex(fis);
} catch (IOException e) {
throw new IllegalStateException("Fail to compute md5 of " + this.file, e);

+ 1
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerPluginInstallerTest.java Ver fichero

@@ -35,6 +35,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.http.DefaultScannerWsClient;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

+ 0
- 251
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java Ver fichero

@@ -1,251 +0,0 @@
/*
* 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 com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.System2;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.HttpConnector;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class ScannerWsClientProviderTest {

public static final GlobalAnalysisMode GLOBAL_ANALYSIS_MODE = new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()));
public static final AnalysisWarnings ANALYSIS_WARNINGS = warning -> {
};
private final Map<String, String> scannerProps = new HashMap<>();

private final ScannerWsClientProvider underTest = new ScannerWsClientProvider();
private final EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3");
public static final String PROXY_AUTH_ENABLED = "proxy-auth";

@RegisterExtension
static WireMockExtension sonarqubeMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

@RegisterExtension
static WireMockExtension proxyMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

private final System2 system2 = mock(System2.class);
private final Properties systemProps = new Properties();

@BeforeEach
void configureMocks(TestInfo info) {
when(system2.properties()).thenReturn(systemProps);

if (info.getTags().contains(PROXY_AUTH_ENABLED)) {
proxyMock.stubFor(get(urlMatching("/api/plugins/.*"))
.inScenario("Proxy Auth")
.whenScenarioStateIs(STARTED)
.willReturn(aResponse()
.withStatus(407)
.withHeader("Proxy-Authenticate", "Basic realm=\"Access to the proxy\""))
.willSetStateTo("Challenge returned"));
proxyMock.stubFor(get(urlMatching("/api/plugins/.*"))
.inScenario("Proxy Auth")
.whenScenarioStateIs("Challenge returned")
.willReturn(aResponse().proxiedFrom(sonarqubeMock.baseUrl())));
} else {
proxyMock.stubFor(get(urlMatching("/api/plugins/.*")).willReturn(aResponse().proxiedFrom(sonarqubeMock.baseUrl())));
}
}

@Test
void provide_client_with_default_settings() {
DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

assertThat(client).isNotNull();
assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/");
HttpConnector httpConnector = (HttpConnector) client.wsConnector();
assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/");
assertThat(httpConnector.okHttpClient().proxy()).isNull();
assertThat(httpConnector.okHttpClient().connectTimeoutMillis()).isEqualTo(5_000);
assertThat(httpConnector.okHttpClient().readTimeoutMillis()).isEqualTo(60_000);

// Proxy is not accessed
assertThat(proxyMock.findAllUnmatchedRequests()).isEmpty();
}

@Test
void provide_client_with_custom_settings() {
scannerProps.put("sonar.host.url", "https://here/sonarqube");
scannerProps.put("sonar.token", "testToken");
scannerProps.put("sonar.ws.timeout", "42");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

assertThat(client).isNotNull();
HttpConnector httpConnector = (HttpConnector) client.wsConnector();
assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/");
assertThat(httpConnector.okHttpClient().proxy()).isNull();
}

@Test
void it_should_timeout_on_long_response() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.responseTimeout", "PT0.2S");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200)
.withFixedDelay(2000)
.withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();

var getRequest = new GetRequest("api/plugins/installed");
var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));

assertThat(thrown).hasStackTraceContaining("timeout");
}

@Test
void it_should_timeout_on_slow_response() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.socketTimeout", "PT0.2S");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200)
.withChunkedDribbleDelay(2, 2000)
.withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();

var getRequest = new GetRequest("api/plugins/installed");
var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));

assertThat(thrown).hasStackTraceContaining("timeout");
}

@Test
void it_should_honor_scanner_proxy_settings() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "" + proxyMock.getPort());

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}

proxyMock.verify(getRequestedFor(urlEqualTo("/api/plugins/installed")));
}

@Test
void it_should_throw_if_invalid_proxy_port() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "not_a_number");
var scannerPropertiesBean = new ScannerProperties(scannerProps);

assertThrows(IllegalArgumentException.class, () -> underTest.provide(scannerPropertiesBean, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS));
}

@Test
@Tag(PROXY_AUTH_ENABLED)
void it_should_honor_scanner_proxy_settings_with_auth() {
var proxyLogin = "proxyLogin";
var proxyPassword = "proxyPassword";
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "" + proxyMock.getPort());
scannerProps.put("sonar.scanner.proxyUser", proxyLogin);
scannerProps.put("sonar.scanner.proxyPassword", proxyPassword);

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}

proxyMock.verify(getRequestedFor(urlEqualTo("/api/plugins/installed"))
.withHeader("Proxy-Authorization", equalTo("Basic " + Base64.getEncoder().encodeToString((proxyLogin + ":" + proxyPassword).getBytes(StandardCharsets.UTF_8)))));

}

@Test
@Tag(PROXY_AUTH_ENABLED)
void it_should_honor_old_jvm_proxy_auth_properties() {
var proxyLogin = "proxyLogin";
var proxyPassword = "proxyPassword";
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "" + proxyMock.getPort());
systemProps.put("http.proxyUser", proxyLogin);
systemProps.put("http.proxyPassword", proxyPassword);

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}

proxyMock.verify(getRequestedFor(urlEqualTo("/api/plugins/installed"))
.withHeader("Proxy-Authorization", equalTo("Basic " + Base64.getEncoder().encodeToString((proxyLogin + ":" + proxyPassword).getBytes(StandardCharsets.UTF_8)))));

}
}

+ 67
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/SonarUserHomeProviderTest.java Ver fichero

@@ -0,0 +1,67 @@
/*
* 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.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.sonar.api.utils.System2;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class SonarUserHomeProviderTest {

private final System2 system = mock(System2.class);
private final SonarUserHomeProvider underTest = new SonarUserHomeProvider(system);

@Test
void createTempFolderFromDefaultUserHome(@TempDir Path userHome) {
when(system.envVariable("SONAR_USER_HOME")).thenReturn(null);
when(system.property("user.home")).thenReturn(userHome.toString());

var sonarUserHome = underTest.provide(new ScannerProperties(Map.of()));

assertThat(sonarUserHome.getPath()).isEqualTo(userHome.resolve(".sonar"));
}

@Test
void should_consider_env_variable_over_user_home(@TempDir Path userHome, @TempDir Path sonarUserHomeFromEnv) {
when(system.envVariable("SONAR_USER_HOME")).thenReturn(sonarUserHomeFromEnv.toString());
when(system.property("user.home")).thenReturn(userHome.toString());

var sonarUserHome = underTest.provide(new ScannerProperties(Map.of()));

assertThat(sonarUserHome.getPath()).isEqualTo(sonarUserHomeFromEnv);
}

@Test
void should_consider_scanner_property_over_env_and_user_home(@TempDir Path userHome, @TempDir Path sonarUserHomeFromEnv, @TempDir Path sonarUserHomeFromProps) {
when(system.envVariable("SONAR_USER_HOME")).thenReturn(sonarUserHomeFromEnv.toString());
when(system.property("user.home")).thenReturn(userHome.toString());

var sonarUserHome = underTest.provide(new ScannerProperties(Map.of("sonar.userHome", sonarUserHomeFromProps.toString())));

assertThat(sonarUserHome.getPath()).isEqualTo(sonarUserHomeFromProps);
}

}

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/cache/DefaultAnalysisCacheLoaderTest.java Ver fichero

@@ -34,7 +34,7 @@ import org.mockito.ArgumentCaptor;
import org.sonar.api.scanner.fs.InputProject;
import org.sonar.api.utils.MessageException;
import org.sonar.api.testfixtures.log.LogTester;
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;

sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/DefaultScannerWsClientTest.java → sonar-scanner-engine/src/test/java/org/sonar/scanner/http/DefaultScannerWsClientTest.java Ver fichero

@@ -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 java.time.LocalDateTime;
import java.time.ZoneOffset;
@@ -33,6 +33,8 @@ import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.scanner.bootstrap.GlobalAnalysisMode;
import org.sonar.scanner.bootstrap.ScannerProperties;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.HttpException;
import org.sonarqube.ws.client.MockWsResponse;

+ 413
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/http/ScannerWsClientProviderTest.java Ver fichero

@@ -0,0 +1,413 @@
/*
* 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;

import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
import org.junitpioneer.jupiter.RestoreSystemProperties;
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.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.HttpConnector;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class ScannerWsClientProviderTest {

private static final GlobalAnalysisMode GLOBAL_ANALYSIS_MODE = new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()));
private static final AnalysisWarnings ANALYSIS_WARNINGS = warning -> {
};
@TempDir
private Path sonarUserHomeDir;
private final SonarUserHome sonarUserHome = mock(SonarUserHome.class);
private final Map<String, String> scannerProps = new HashMap<>();

private final ScannerWsClientProvider underTest = new ScannerWsClientProvider();
private final EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3");

private final System2 system2 = mock(System2.class);
private final Properties systemProps = new Properties();

@BeforeEach
void configureMocks() {
when(system2.properties()).thenReturn(systemProps);
when(sonarUserHome.getPath()).thenReturn(sonarUserHomeDir);
}

@Test
void provide_client_with_default_settings() {
DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

assertThat(client).isNotNull();
assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/");
HttpConnector httpConnector = (HttpConnector) client.wsConnector();
assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/");
assertThat(httpConnector.okHttpClient().proxy()).isNull();
assertThat(httpConnector.okHttpClient().connectTimeoutMillis()).isEqualTo(5_000);
assertThat(httpConnector.okHttpClient().readTimeoutMillis()).isEqualTo(60_000);
}

@Test
void provide_client_with_custom_settings() {
scannerProps.put("sonar.host.url", "https://here/sonarqube");
scannerProps.put("sonar.token", "testToken");
scannerProps.put("sonar.ws.timeout", "42");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

assertThat(client).isNotNull();
HttpConnector httpConnector = (HttpConnector) client.wsConnector();
assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/");
assertThat(httpConnector.okHttpClient().proxy()).isNull();
}

@Nested
class WithMockHttpSonarQube {

@RegisterExtension
static WireMockExtension sonarqubeMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

@Test
void it_should_timeout_on_long_response() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.responseTimeout", "PT0.2S");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200)
.withFixedDelay(2000)
.withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();

var getRequest = new GetRequest("api/plugins/installed");
var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));

assertThat(thrown).hasStackTraceContaining("timeout");
}

@Test
void it_should_timeout_on_slow_response() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.socketTimeout", "PT0.2S");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200)
.withChunkedDribbleDelay(2, 2000)
.withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();

var getRequest = new GetRequest("api/plugins/installed");
var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));

assertThat(thrown).hasStackTraceContaining("timeout");
}

@Test
void it_should_throw_if_invalid_proxy_port() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "not_a_number");
var scannerPropertiesBean = new ScannerProperties(scannerProps);

assertThrows(IllegalArgumentException.class, () -> underTest.provide(scannerPropertiesBean, env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome));
}

@Nested
class WithProxy {

private static final String PROXY_AUTH_ENABLED = "proxy-auth";

@RegisterExtension
static WireMockExtension proxyMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();

@BeforeEach
void configureMocks(TestInfo info) {
if (info.getTags().contains(PROXY_AUTH_ENABLED)) {
proxyMock.stubFor(get(urlMatching("/api/plugins/.*"))
.inScenario("Proxy Auth")
.whenScenarioStateIs(STARTED)
.willReturn(aResponse()
.withStatus(407)
.withHeader("Proxy-Authenticate", "Basic realm=\"Access to the proxy\""))
.willSetStateTo("Challenge returned"));
proxyMock.stubFor(get(urlMatching("/api/plugins/.*"))
.inScenario("Proxy Auth")
.whenScenarioStateIs("Challenge returned")
.willReturn(aResponse().proxiedFrom(sonarqubeMock.baseUrl())));
} else {
proxyMock.stubFor(get(urlMatching("/api/plugins/.*")).willReturn(aResponse().proxiedFrom(sonarqubeMock.baseUrl())));
}
}

@Test
void it_should_honor_scanner_proxy_settings() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "" + proxyMock.getPort());

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}

proxyMock.verify(getRequestedFor(urlEqualTo("/api/plugins/installed")));
}

@Test
@Tag(PROXY_AUTH_ENABLED)
void it_should_honor_scanner_proxy_settings_with_auth() {
var proxyLogin = "proxyLogin";
var proxyPassword = "proxyPassword";
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "" + proxyMock.getPort());
scannerProps.put("sonar.scanner.proxyUser", proxyLogin);
scannerProps.put("sonar.scanner.proxyPassword", proxyPassword);

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}

proxyMock.verify(getRequestedFor(urlEqualTo("/api/plugins/installed"))
.withHeader("Proxy-Authorization", equalTo("Basic " + Base64.getEncoder().encodeToString((proxyLogin + ":" + proxyPassword).getBytes(StandardCharsets.UTF_8)))));

}

@Test
@Tag(PROXY_AUTH_ENABLED)
void it_should_honor_old_jvm_proxy_auth_properties() {
var proxyLogin = "proxyLogin";
var proxyPassword = "proxyPassword";
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");
scannerProps.put("sonar.scanner.proxyPort", "" + proxyMock.getPort());
systemProps.put("http.proxyUser", proxyLogin);
systemProps.put("http.proxyPassword", proxyPassword);

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}

proxyMock.verify(getRequestedFor(urlEqualTo("/api/plugins/installed"))
.withHeader("Proxy-Authorization", equalTo("Basic " + Base64.getEncoder().encodeToString((proxyLogin + ":" + proxyPassword).getBytes(StandardCharsets.UTF_8)))));

}
}

}

@Nested
class WithMockHttpsSonarQube {

public static final String KEYSTORE_PWD = "pwdServerP12";

@RegisterExtension
static WireMockExtension sonarqubeMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicHttpsPort().httpDisabled(true)
.keystoreType("pkcs12")
.keystorePath(toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/server.p12"))).toString())
.keystorePassword(KEYSTORE_PWD)
.keyManagerPassword(KEYSTORE_PWD))
.build();

@BeforeEach
void mockResponse() {
sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));
}

@Test
void it_should_not_trust_server_self_signed_certificate_by_default() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
var getRequest = new GetRequest("api/plugins/installed");
var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));

assertThat(thrown).hasStackTraceContaining("CertificateException");
}

@Test
void it_should_trust_server_self_signed_certificate_when_certificate_is_in_truststore() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.truststorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client-truststore.p12"))).toString());
scannerProps.put("sonar.scanner.truststorePassword", "pwdClientWithServerCA");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}
}
}

@Nested
class WithMockHttpsSonarQubeAndClientCertificates {

public static final String KEYSTORE_PWD = "pwdServerP12";

@RegisterExtension
static WireMockExtension sonarqubeMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicHttpsPort().httpDisabled(true)
.keystoreType("pkcs12")
.keystorePath(toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/server.p12"))).toString())
.keystorePassword(KEYSTORE_PWD)
.keyManagerPassword(KEYSTORE_PWD)
.needClientAuth(true)
.trustStoreType("pkcs12")
.trustStorePath(toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/server-with-client-ca.p12"))).toString())
.trustStorePassword("pwdServerWithClientCA"))
.build();

@BeforeEach
void mockResponse() {
sonarqubeMock.stubFor(get("/api/plugins/installed")
.willReturn(aResponse().withStatus(200).withBody("Success")));
}

@Test
void it_should_fail_if_client_certificate_not_provided() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.truststorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client-truststore.p12"))).toString());
scannerProps.put("sonar.scanner.truststorePassword", "pwdClientWithServerCA");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
var getRequest = new GetRequest("api/plugins/installed");
var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));

assertThat(thrown).satisfiesAnyOf(
e -> assertThat(e).hasStackTraceContaining("SSLHandshakeException"),
// Exception is flaky because of https://bugs.openjdk.org/browse/JDK-8172163
e -> assertThat(e).hasStackTraceContaining("Broken pipe"));
}

@Test
void it_should_authenticate_using_certificate_in_keystore() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());

scannerProps.put("sonar.scanner.truststorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client-truststore.p12"))).toString());
scannerProps.put("sonar.scanner.truststorePassword", "pwdClientWithServerCA");
scannerProps.put("sonar.scanner.keystorePath", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client.p12"))).toString());
scannerProps.put("sonar.scanner.keystorePassword", "pwdClientCertP12");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}
}

@RestoreSystemProperties
@Test
void it_should_support_jvm_system_properties() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
System.setProperty("javax.net.ssl.trustStore", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client-truststore.p12"))).toString());
System.setProperty("javax.net.ssl.trustStorePassword", "pwdClientWithServerCA");
System.setProperty("javax.net.ssl.keyStore", toPath(requireNonNull(ScannerWsClientProviderTest.class.getResource("/ssl/client.p12"))).toString());
systemProps.setProperty("javax.net.ssl.keyStore", "any value is fine here");
System.setProperty("javax.net.ssl.keyStorePassword", "pwdClientCertP12");

DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS, sonarUserHome);

HttpConnector httpConnector = (HttpConnector) client.wsConnector();
try (var r = httpConnector.call(new GetRequest("api/plugins/installed"))) {
assertThat(r.code()).isEqualTo(200);
assertThat(r.content()).isEqualTo("Success");
}
}
}

private static Path toPath(URL url) {
try {
return Paths.get(url.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/platform/DefaultServerTest.java Ver fichero

@@ -24,7 +24,7 @@ import org.sonar.api.CoreProperties;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.utils.Version;
import org.sonar.core.platform.SonarQubeVersion;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/qualitygate/QualityGateCheckTest.java Ver fichero

@@ -33,7 +33,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.event.Level;
import org.sonar.api.testfixtures.log.LogTesterJUnit5;
import org.sonar.api.utils.MessageException;
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;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java Ver fichero

@@ -39,7 +39,7 @@ import org.sonar.api.platform.Server;
import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.TempFolder;
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.ci.DevOpsPlatformInfo;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultMetricsRepositoryLoaderTest.java Ver fichero

@@ -26,7 +26,7 @@ import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultNewCodePeriodLoaderTest.java Ver fichero

@@ -25,7 +25,7 @@ import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonarqube.ws.NewCodePeriods;

import static org.mockito.Mockito.mock;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultProjectRepositoriesLoaderTest.java Ver fichero

@@ -30,7 +30,7 @@ import org.junit.Test;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.utils.MessageException;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonarqube.ws.Batch.WsProjectResponse;
import org.sonarqube.ws.client.HttpException;


+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/DefaultQualityProfileLoaderTest.java Ver fichero

@@ -26,7 +26,7 @@ import java.io.InputStream;
import org.junit.Test;
import org.sonar.api.utils.MessageException;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonarqube.ws.Qualityprofiles;
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile;
import org.sonarqube.ws.client.HttpException;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/language/DefaultLanguagesRepositoryTest.java Ver fichero

@@ -28,7 +28,7 @@ import org.slf4j.event.Level;
import org.sonar.api.config.Configuration;
import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.catchThrowableOfType;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/settings/DefaultGlobalSettingsLoaderTest.java Ver fichero

@@ -25,7 +25,7 @@ import java.io.PipedOutputStream;
import java.util.Map;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
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.WsResponse;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/settings/DefaultProjectSettingsLoaderTest.java Ver fichero

@@ -25,7 +25,7 @@ import java.io.PipedOutputStream;
import java.util.Map;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonar.scanner.bootstrap.ScannerProperties;
import org.sonarqube.ws.Settings;
import org.sonarqube.ws.client.GetRequest;

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java Ver fichero

@@ -30,7 +30,7 @@ import org.sonar.api.batch.rule.LoadedActiveRule;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.bootstrap.DefaultScannerWsClient;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonar.scanner.scan.branch.BranchConfiguration;
import org.sonarqube.ws.Common;
import org.sonarqube.ws.Rules;

+ 452
- 0
sonar-scanner-engine/src/test/resources/ssl/README.md Ver fichero

@@ -0,0 +1,452 @@
## Let's create TLS certificates

The most common format of certificates are PEM, so let's generate them instead of using Java keytool (that can also generate keys in JKS format).

This README, is a *simplified* version for generating the certificates only for test's purposes.

**DO NOT USE IT FOR PRODUCTION**

### Generation of a TLS server certificate

In this example the configuration of OpenSSL is entirely in openssl.conf (a stripped version of openssl.cnf that may vary from distribution to distribution)

#### First let's create a Certificate Authority

The Certificate Authority is a private key that is used to sign other X.509 certificates in order to validate the ownership of a website (trusted tier).

```bash
$ openssl genrsa -out ca.key 4096
.....++
................................................................................................................................................++
e is 65537 (0x010001)
```

Now we have our key to sign other certificates : `ca.key` in PEM format.

Let's create our X.509 CA certificate :

```bash
$ openssl req -key ca.key -new -x509 -days 3650 -sha256 -extensions ca_extensions -out ca.crt -config ./openssl.conf
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2-letter code) [CH]:
State or Province Name (full name) [Geneva]:
Locality (e.g. city name) [Geneva]:
Organization (e.g. company name) [SonarSource SA]:
Common Name (your.domain.com) [localhost]:
```

There is no important values here.

#### Let's create a self-signed certificate our TLS server using our CA

We want to create a X.509 certificate for our https server. This certificate will be a Certificate Signing Request. A certificate that need to be signed by a trusted tier.
The default configuration is set in `openssl.conf` and it has been configuration for `localhost`.
The most important part is the `Common Name` and `DNS.1` (set in `openssl.conf`).

So just keep using enter with this command line :

```bash
$ openssl req -new -keyout server.key -out server.csr -nodes -newkey rsa:4096 -config ./openssl.conf
Generating a 4096 bit RSA private key
........................................................................++
.........................................................................................++
writing new private key to 'server.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2-letter code) [CH]:
State or Province Name (full name) [Geneva]:
Locality (e.g. city name) [Geneva]:
Organization (e.g. company name) [SonarSource SA]:
Common Name (your.domain.com) [localhost]:
```

No we have `server.csr` file valid for 10 years.
Let's see what's in this certificate :
```bash
$ openssl req -verify -in server.csr -text -noout
verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = CH, ST = Geneva, L = Geneva, O = SonarSource SA, CN = localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:c8:2d:dc:64:1a:b6:d9:a9:3e:bd:3f:d3:ae:27:
ab:00:a8:09:f7:9e:ae:b5:70:c0:11:ab:2d:45:48:
6c:b9:b3:b1:4b:42:b7:4e:48:d3:2e:38:cb:e5:7d:
14:30:d3:b8:1d:2f:e2:09:04:cc:aa:80:09:51:bc:
59:9d:a7:7a:76:34:cc:7a:2b:ae:d3:ef:98:38:ef:
b2:8a:0e:e9:2f:79:4e:d4:a9:10:63:2b:5b:05:05:
ef:6b:98:41:e3:c0:3e:6c:5f:8a:66:10:ca:98:e5:
37:c6:ea:13:48:c9:92:22:53:44:1a:61:27:f4:60:
16:a7:a9:87:a9:d3:cf:88:5e:d4:47:44:24:4f:6d:
5e:c0:4a:ff:ad:e4:82:63:da:82:eb:9e:b3:76:6f:
5d:b4:2d:fc:96:4a:98:e4:f5:20:97:48:38:11:29:
33:7d:5a:96:fa:28:49:9f:cb:24:f8:02:f6:bb:ed:
f3:91:90:51:10:c2:93:28:56:6e:4d:51:51:10:27:
8f:c3:f0:cd:ee:51:2d:dc:e5:a7:21:55:20:44:ac:
8b:66:1d:b7:eb:e0:ed:69:f0:d4:32:82:ee:53:91:
3b:ee:58:83:ba:3b:9d:3f:f7:23:0e:36:46:20:6b:
6a:80:9b:11:46:28:39:60:25:69:9e:e5:d0:34:ba:
2b:c3:33:f2:44:3d:fb:8f:2d:47:a6:ae:64:9a:b3:
5a:f0:ed:cb:3e:86:33:80:23:32:d0:e7:51:91:a8:
c6:97:d1:7c:e4:02:52:5d:7c:a9:97:83:00:c5:10:
fb:13:f9:29:1f:79:c4:a5:8c:7b:64:e0:cd:b6:a1:
34:36:aa:f4:63:63:77:12:d3:fa:fe:1d:54:2e:64:
43:38:a2:71:28:72:7a:bf:33:cb:8c:27:a7:66:51:
8f:6f:e8:d2:90:19:2f:d4:8e:ac:b4:7b:e0:53:a8:
0f:11:d1:7d:08:71:de:0a:a4:63:10:79:c8:e8:bf:
7e:be:8b:06:7d:43:9b:4b:a1:0a:49:a6:c8:c6:43:
c4:24:23:13:2a:b2:f9:f2:b8:e7:8e:ab:3e:2a:b5:
50:26:23:d6:b2:d3:ee:23:ec:d1:36:92:70:2e:df:
82:6a:d2:07:bb:f0:97:51:42:e4:d8:49:69:35:bb:
38:90:1f:8e:aa:1d:27:78:26:26:d4:36:75:ee:83:
17:69:cb:7f:53:45:8f:b4:63:13:d5:fd:42:10:8a:
d3:75:38:4a:bd:13:cf:68:5e:41:6d:f0:57:b5:75:
e3:dc:10:82:ab:29:ed:a1:27:9c:50:74:f2:4c:4a:
a3:78:2a:53:ca:90:a6:89:20:24:85:b5:ec:c9:c7:
be:96:b5
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:localhost
X509v3 Key Usage:
Digital Signature, Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
Signature Algorithm: sha256WithRSAEncryption
bf:9d:6e:2f:cc:40:9b:92:29:c2:f1:0a:85:6c:35:eb:8e:fa:
13:0c:53:58:33:5f:7b:09:58:5f:dd:94:7e:2c:65:ed:73:91:
2a:6b:cc:2d:ec:26:1c:8e:95:57:d9:35:19:82:4f:42:59:81:
d9:b7:bb:08:70:28:70:35:50:f6:6a:46:e0:2a:ab:90:50:5a:
dc:b0:c3:b8:52:d7:5c:90:8f:4c:61:09:2c:ba:4a:31:37:6f:
e0:b9:6b:98:dd:aa:dd:52:66:7e:06:f1:8a:4b:bc:23:0d:62:
d3:b9:86:8f:3e:cc:05:2b:4d:c4:ad:cf:ae:be:33:22:f6:95:
00:f0:36:96:26:5e:42:84:d0:2a:79:41:1e:18:10:1c:96:3e:
9a:8b:cc:a5:f9:59:5b:78:d0:a1:a5:2e:4d:55:30:10:0b:cd:
13:bc:75:9a:49:e0:de:a4:4d:ed:9b:e8:42:2f:74:2b:dc:6f:
2d:d3:38:a9:e8:f8:98:2c:56:aa:3e:dd:0d:48:78:16:4c:50:
fd:0a:b3:3c:28:ac:64:7e:e9:bb:10:0e:3b:29:68:40:a9:19:
5a:2c:5c:d6:7e:32:39:96:49:a7:4c:6a:a6:09:8e:d4:b8:1e:
3e:2c:93:c3:2c:da:f2:09:20:ef:f4:a9:d2:ff:de:cd:7b:20:
66:46:ff:c2:36:c3:7d:32:d6:55:d1:fe:0f:00:9a:23:56:97:
52:a1:0a:52:64:29:50:c7:5d:b4:1e:e4:67:9a:07:3f:fb:85:
03:00:22:d8:f5:e6:bc:95:bf:bc:08:ab:4d:32:4c:d6:52:e0:
72:3e:8a:a5:85:72:43:d6:d4:51:6e:99:9a:1f:d8:0e:fd:4d:
59:81:7e:c1:81:6d:3b:69:76:ce:53:a4:c0:69:46:72:b2:fe:
40:b3:a5:5c:b0:ce:d2:61:83:be:0f:c3:85:a0:21:a7:e8:fd:
2f:2c:1c:68:24:1d:9b:a3:43:cb:5e:30:21:af:e8:2e:4e:ec:
ea:a7:d2:68:f1:bd:3f:3c:41:48:ac:91:f9:9d:e8:f2:3d:cb:
d0:82:d2:00:ed:7b:fa:d8:98:e3:a8:74:f2:ce:70:95:0a:9d:
c2:b2:cc:08:d1:fd:de:26:d3:3e:c0:62:28:9b:b4:2d:f4:b5:
6d:48:c9:d3:05:f5:1e:68:17:6b:fb:02:2e:20:98:1a:de:d4:
ae:6b:e0:68:97:98:e0:4f:47:ec:14:fd:dc:57:d2:e2:5c:59:
36:a5:0b:94:b7:4e:b8:ae:ee:c9:ac:02:ae:43:bf:9f:07:da:
0c:44:b0:47:69:1d:64:ea:bd:68:af:4f:a7:9a:1f:b1:b9:1d:
71:0e:86:4e:0c:ff:a3:1d
```

#### Let's sign this certificate with our own CA

The CSR will be signed with our previously created ca.key
We'll sign it to be valid for 10years (3650)

```bash
$ openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.pem -sha256 -extfile v3.ext
Signature ok
subject=C = CH, ST = Geneva, L = Geneva, O = SonarSource SA, CN = localhost
Getting CA Private Key
```

Let's verify what are in this certificate :

```bash
$ openssl x509 -in server.pem -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
d5:c5:2a:c2:c8:f6:43:c7
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = CH, ST = Geneva, L = Geneva, O = SonarSource SA, CN = SonarSource SA
Validity
Not Before: Mar 17 14:12:29 2020 GMT
Not After : Mar 15 14:12:29 2030 GMT
Subject: C = CH, ST = Geneva, L = Geneva, O = SonarSource SA, CN = localhost
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:a2:43:1e:8b:60:b5:e0:61:3e:99:a4:54:93:c8:
16:14:c2:fa:fd:e5:7c:05:02:71:09:46:d9:2a:52:
57:12:d7:74:46:6a:bd:d4:de:4a:06:b2:51:83:2c:
98:07:8c:b0:f7:e1:8a:aa:fc:0c:30:c6:d7:ec:57:
0b:a7:12:45:e3:13:1a:26:e8:22:d8:fd:2a:9e:ae:
7b:20:b8:41:99:50:0e:b7:1c:bb:78:18:60:25:67:
78:5b:af:d8:7f:d1:01:12:81:0a:1f:dd:f0:54:bc:
57:16:05:22:7c:65:a2:7e:03:ed:e8:7f:50:b1:cd:
7c:e8:7b:58:cb:df:6d:e3:04:03:78:a4:83:e7:20:
c4:37:bc:00:ba:7c:12:d9:ac:52:88:88:72:df:fc:
35:8f:94:f0:1b:33:f8:94:b8:bc:ab:0e:89:68:5f:
92:1b:af:c9:da:c2:c2:e2:a1:c3:8e:c8:16:1a:9e:
89:7a:b4:24:2c:24:df:c5:26:59:ab:d8:f9:06:39:
02:c0:0d:88:5a:0c:14:e7:bc:c5:b8:4c:e5:e0:85:
b2:0b:88:36:b3:d5:35:10:e9:b8:5a:48:69:1a:b3:
2a:4a:d6:f3:f5:6a:91:41:f8:1e:da:d0:0e:21:c3:
a2:f8:5c:08:42:a2:2b:13:be:63:e5:67:d5:19:2f:
2c:96:6d:17:1c:7f:34:19:68:cf:91:b6:14:d9:9a:
1b:1c:f9:08:d7:f9:2d:c3:48:14:3d:02:d4:90:f7:
f2:74:65:f8:22:2d:46:b2:76:cd:46:c1:8e:ab:a1:
11:d7:12:14:77:e3:1c:c3:1c:fa:32:79:0e:0e:59:
55:e4:9d:60:d7:18:0b:25:82:97:28:30:df:de:89:
5b:56:37:a2:33:86:26:12:83:75:f0:02:ae:88:b5:
d6:5e:a2:b7:e7:57:9d:de:72:ad:d6:55:2a:e1:a8:
4c:15:18:a9:e3:22:52:f1:74:e1:b0:d2:e7:9b:ec:
f9:6d:5f:86:c2:9c:e2:22:f2:f4:11:a2:d1:71:b8:
77:e4:8c:4c:ed:84:e8:f9:82:a2:f1:73:95:19:08:
92:d5:b3:50:be:bc:c2:ec:0e:d7:da:53:d2:22:36:
c8:d8:48:d1:22:0d:42:a7:68:6d:e5:b6:5f:00:7d:
70:e4:5f:fe:df:db:3a:96:30:c8:76:89:e9:d1:98:
1e:63:e2:d0:29:46:b0:3d:f6:38:d7:07:40:47:0e:
a3:a5:70:1c:8b:80:c1:81:d1:35:cd:3d:93:20:c6:
7c:10:a4:09:ed:41:12:2e:c3:66:e5:47:96:58:de:
53:1b:d5:67:2c:1d:55:3b:c1:03:28:cf:5e:aa:33:
2b:8c:e1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:26:4F:F6:F9:E6:8B:B6:F7:59:CE:30:23:5C:90:2E:AE:7A:20:C4:DB

X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Subject Alternative Name:
DNS:localhost
Signature Algorithm: sha256WithRSAEncryption
b0:df:99:da:44:e1:22:c6:51:da:e1:b5:a9:fd:fe:82:d6:74:
07:ad:d4:b4:f8:29:3e:57:7a:1b:98:36:4e:0a:23:68:f5:27:
c7:52:59:90:cd:94:23:08:83:6f:a4:af:14:a3:e3:ed:f2:13:
e4:17:f7:7c:27:45:bc:8c:9a:1d:f3:90:c6:b4:3e:e8:7a:c1:
18:e4:8e:8c:28:ac:02:c7:d1:4c:e3:67:7a:13:69:ff:a4:74:
c4:82:d7:54:d3:cb:7b:4e:f9:25:36:90:33:43:f0:b8:a5:e6:
7c:ea:3d:41:fe:51:3c:bc:d2:c6:4e:9c:dc:04:69:23:08:70:
bf:69:2a:bd:28:8c:3f:a1:f0:b0:88:87:a2:af:63:85:86:e3:
07:2a:74:89:d0:69:b3:8c:7d:a5:db:ec:f2:5c:56:33:89:04:
c6:75:a9:a2:b8:c0:1b:b5:dd:0f:96:50:71:ad:39:36:39:13:
d0:80:f3:c8:50:db:d2:65:4d:56:75:9c:70:c2:d6:0c:6b:4a:
6e:f7:f1:76:1b:82:16:13:eb:37:4f:05:fd:8f:06:89:15:d7:
6d:a7:4e:43:bb:ee:b1:a8:c0:f4:cd:d7:1f:17:c3:3f:1a:79:
8f:6e:46:a4:e5:1f:82:8d:60:6f:6c:a2:f4:9b:6e:59:85:48:
73:ae:78:dd:c1:fa:81:1f:38:56:84:fc:31:98:af:a8:e4:bf:
62:45:16:38:4a:5d:0e:6a:c4:bf:e1:9b:2b:c4:eb:dc:d4:85:
82:0f:6c:31:54:1c:46:62:51:22:c3:0d:e4:ca:2e:c9:5f:f5:
8c:7a:8c:c2:1d:f2:a8:f9:65:e6:ca:4e:6d:21:4e:55:07:6c:
58:0d:fd:59:76:9c:65:7f:26:8f:8b:7b:01:70:5f:59:25:66:
a8:9b:0a:70:a1:d8:fd:61:26:7e:4d:5f:3c:28:74:2b:94:fb:
2a:8e:35:51:77:5a:96:a9:9b:4e:18:b6:6d:0b:55:4e:2e:15:
ca:e7:cb:15:29:0e:b9:fd:23:56:a7:ad:dc:a1:b9:1b:1b:19:
24:10:e3:a5:cb:69:2b:40:74:3c:3e:31:ac:a9:0d:17:6b:51:
61:d4:5e:d1:98:b6:81:29:55:92:1f:00:8d:4c:72:d4:3a:0e:
fd:1f:30:73:04:b8:99:6f:27:57:9a:6c:2b:e1:fa:c2:d3:bf:
d3:d2:24:f3:5c:30:a3:25:d6:f5:18:91:13:d4:55:1e:33:89:
b7:99:27:a9:14:e4:d9:32:50:ba:56:2f:53:b7:a1:d7:d3:14:
2f:e2:73:5a:d4:b2:94:73:14:ef:ac:6f:a1:c1:84:31:17:fd:
fa:f8:62:d3:eb:a5:8a:34
```

#### Let's create a PKCS12 file to be used for starting a TLS server

```bash
$ openssl pkcs12 -export -in server.pem -inkey server.key -name localhost -out server.p12
Enter Export Password: pwdServerP12
Verifying - Enter Export Password: pwdServerP12
```

The password of the PKCS12 file is `pwdServerP12`

The `server.p12` file can now be used to start a TLS server.

#### Now we'll generate the `client-truststore.p12` file that will have the server CA certificate.
Since we don't need to add the key of the certificate (only required to sign, not to verify), we can import it directly with keytool.

```bash
$ keytool -import -trustcacerts -alias server-ca -keystore client-truststore.p12 -file ca.crt
Enter keystore password: pwdClientWithServerCA
Re-enter new password: pwdClientWithServerCA
Owner: CN=SonarSource, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH
Issuer: CN=SonarSource, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH
Serial number: ed8bcadd4888ffac
Valid from: Sat Sep 15 08:10:22 CEST 2018 until: Tue Sep 12 08:10:22 CEST 2028
Certificate fingerprints:
MD5: 25:38:06:14:D0:B3:36:81:65:FC:44:CA:E3:BA:57:12
SHA1: 77:56:EF:C7:2F:5A:29:D1:A0:54:5F:F8:B4:19:60:91:7B:71:E4:2C
SHA256: 1D:2D:E5:52:21:60:75:08:F3:0A:B3:93:CF:38:F6:30:88:56:28:73:20:BA:76:9A:C0:A1:D7:8C:4D:D3:84:AA
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 87 B9 C1 23 E2 F1 A3 68 BD D6 44 99 0E AD FC FC ...#...h..D.....
0010: A5 31 90 D4 .1..
]
]

#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]

#3: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
]

#4: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
Key_Encipherment
Data_Encipherment
Key_CertSign
Crl_Sign
]

#5: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 87 B9 C1 23 E2 F1 A3 68 BD D6 44 99 0E AD FC FC ...#...h..D.....
0010: A5 31 90 D4 .1..
]
]

Trust this certificate? [no]: yes
Certificate was added to keystore
```

### Create a certificate that will be used to authenticate a user

The principle is the same we'll have a CA authority signing certificates that will be sent by the user to the server.
In this case the server will have to host the CA authority in its TrustedKeyStore while the client will host his certificate in is KeyStore.
In this use case, the extensions are not the same, so we'll use openssl-client-auth.conf

#### Generation of CA

One line to generate both the key `ca-lient-auth.key` and the CA certificate `ca-client-auth.crt`

```bash
openssl req -newkey rsa:4096 -nodes -keyout ca-client-auth.key -new -x509 -days 3650 -sha256 -extensions ca_extensions -out ca-client-auth.crt -subj '/C=CH/ST=Geneva/L=Geneva/O=SonarSource SA/CN=SonarSource/' -config ./openssl-client-auth.conf
Generating a 4096 bit RSA private key
...................................++
............................................................................................................................................................................................................................................................++
writing new private key to 'ca-client-auth.key'
-----

```

For the certificate, the Common Name is used to identify the user
```bash
$ openssl req -new -keyout client.key -out client.csr -nodes -newkey rsa:4096 -subj '/C=CH/ST=Geneva/L=Geneva/O=SonarSource SA/CN=Julien Henry/' -config ./openssl-client-auth.conf
Generating a 4096 bit RSA private key
..............................................++
................++
writing new private key to 'client.key'
-----
```

Let's sign this certificate
```bash
$ openssl x509 -req -days 3650 -in client.csr -CA ca-client-auth.crt -CAkey ca-client-auth.key -CAcreateserial -out client.pem -sha256
Signature ok
subject=C = CH, ST = Geneva, L = Geneva, O = SonarSource SA, CN = Julien Henry
Getting CA Private Key
```

Let's create the pkcs12 store containing the client certificate

```bash
$ openssl pkcs12 -export -in client.pem -inkey client.key -name julienhenry -out client.p12
Enter Export Password: pwdClientCertP12
Verifying - Enter Export Password: pwdClientCertP12
```

This will go to client keyStore.
Now we'll generate the `server-with-client-ca.p12` file that will have the CA certificate. Since we don't need to add the key of the certificate (only required to sign, not to verify), we can import it directly with keytool.

```bash
$ keytool -import -trustcacerts -alias client-ca -keystore server-with-client-ca.p12 -file ca-client-auth.crt
Enter keystore password: pwdServerWithClientCA
Re-enter new password: pwdServerWithClientCA
Owner: CN=SonarSource, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH
Issuer: CN=SonarSource, O=SonarSource SA, L=Geneva, ST=Geneva, C=CH
Serial number: ed8bcadd4888ffac
Valid from: Sat Sep 15 08:10:22 CEST 2018 until: Tue Sep 12 08:10:22 CEST 2028
Certificate fingerprints:
MD5: 25:38:06:14:D0:B3:36:81:65:FC:44:CA:E3:BA:57:12
SHA1: 77:56:EF:C7:2F:5A:29:D1:A0:54:5F:F8:B4:19:60:91:7B:71:E4:2C
SHA256: 1D:2D:E5:52:21:60:75:08:F3:0A:B3:93:CF:38:F6:30:88:56:28:73:20:BA:76:9A:C0:A1:D7:8C:4D:D3:84:AA
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 4096-bit RSA key
Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 87 B9 C1 23 E2 F1 A3 68 BD D6 44 99 0E AD FC FC ...#...h..D.....
0010: A5 31 90 D4 .1..
]
]

#2: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]

#3: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
]

#4: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
DigitalSignature
Key_Encipherment
Data_Encipherment
Key_CertSign
Crl_Sign
]

#5: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 87 B9 C1 23 E2 F1 A3 68 BD D6 44 99 0E AD FC FC ...#...h..D.....
0010: A5 31 90 D4 .1..
]
]

Trust this certificate? [no]: yes
Certificate was added to keystore

```

+ 33
- 0
sonar-scanner-engine/src/test/resources/ssl/ca-client-auth.crt Ver fichero

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFvzCCA6egAwIBAgIUXBeKw1w29u0HMpY3ywBefre2RewwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTEUMBIGA1UEAwwLU29uYXJTb3Vy
Y2UwHhcNMjQwNDE2MDkzNTM3WhcNMzQwNDE0MDkzNTM3WjBeMQswCQYDVQQGEwJD
SDEPMA0GA1UECAwGR2VuZXZhMQ8wDQYDVQQHDAZHZW5ldmExFzAVBgNVBAoMDlNv
bmFyU291cmNlIFNBMRQwEgYDVQQDDAtTb25hclNvdXJjZTCCAiIwDQYJKoZIhvcN
AQEBBQADggIPADCCAgoCggIBAM1czueBEjAXOWZcZiX6wpqMNqExQHnoMnG+wvzc
hCHHEkVQIUh/TSmvopfOuuSnr3EXvydXBZS4phmsobNKkZ02mSQNObiB5e0cxntI
y5CxaMerhT/JJuGxh/+Imwru/F/x/ErCiHqn92fw4yTstGqSP73fjdCBJcuLyRGa
nYzCykCN5GCvSQVglS62RaNSmBDcL4XEGYdO6PRZVbJ5k0luzK4knPc0s5Sd0eo5
VzPBvkRHpAcRSUfunGn75rgC5Izy84nDA4KbOk2d9tQMpMHWs9Y5Z4qZJsII3dXz
ZH4k8jJzt3VWvHbG/1+CmUdEW/62AK3aVLtmuygsD1NHMALIaF96bf22Ga646OFK
Qw9FGHr4d3+LbG4ZvdNhPByPtZC5YU4XMk7agnH6k0O2szwEP53eGp+YWSKcZ3o9
m2XnTfOyUzfF8luDww6qrpmNkuSfQVGhWevSQRNSjz5esFKhdW3Upnx5WY0jf9KQ
n9Tsw6yJ6GG/ZzRL5uMKunyfrEeCXYZWAlQ1G2DSLQw0+L1ysbWn49NNw8P1l3xi
RbegcR4QSreBsf65u7ZRV1iaECRD2uvylqxK/5Y6me+0bfAZ1set974N8pyDiuXc
LEeKnfo5TcHhXEfJEOfSGrGZKgV+54FOzoOcy088gPkRIUykbghJC4vIiKzgwp/i
1QlZAgMBAAGjdTBzMAsGA1UdDwQEAwIBtjATBgNVHSUEDDAKBggrBgEFBQcDATAd
BgNVHQ4EFgQUbAPAj0KGCnTshl0HEOdGteNZvTEwHwYDVR0jBBgwFoAUbAPAj0KG
CnTshl0HEOdGteNZvTEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AgEAGIKrSLleiv8rUcysRdB0hkiLLNFCDR1/DgZd+ZHmqGkeZpAdS4Q+Y1PgKewQ
nave2KbCTjoRsh8EBU0/ZVKDXKs2IN63c0B0Bc5GYqYq27vBiVrkkPGawWjHaI8M
AdESBtGBgd4F2SP9PQNFuLSj986NTu4WiRULwHKHidBwQVE6nDWif4TSWrELDbG8
VcP1uWNLT68hMGBrbNL1vyScZImUqNyRMMtbQb9IBF7e0wgcIm5xewu/MQwYQAMG
ap8lalHjfTrb3DtOV/pjQEI6c8xTvPF6w0FfPUE6ViXipn4Z7yFbPbIrRlcAmcG9
YW4fflIza+x06IvqTHZVYH+G3xyA6V9Qq/dSniwp8mJU+g3aeMMWa5M1xgEO+4B1
Xv4Fj3Z3YfU50ZBycoXVxAvDOXSoGnR+Q4Wgk5G/qvXX3ZBd3U3mieOkJ8L31pHk
i2KPuYtm8ynrRc4InqAxKCJkFG8ylqnNCQ+Hl9qfa+PQzeGxwK5/wjztF/rjOYep
eed+0DbpV7dwxVhIhBbdu++rjGejzB6qRORwYoGoHYLX6LxdK4LU8g3eLxA/SKs8
8jeXpNYI8ucX6EkSZAPkvNfgQw5Akbd8oNf8rJvpGdV3vb4+yraZH400Do5+SUeu
VHlCQX+vlBXfZbQAgRuFaXj2AWu2Ipp6DLYA1Hx89b3190Y=
-----END CERTIFICATE-----

+ 52
- 0
sonar-scanner-engine/src/test/resources/ssl/ca-client-auth.key Ver fichero

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
53j8dPRgcqZGjgO/EG0uAj3DINgNOg==
-----END PRIVATE KEY-----

+ 33
- 0
sonar-scanner-engine/src/test/resources/ssl/ca.crt Ver fichero

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIUQ4l+juBLIywkaUyvd+3hdgWlND4wDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0
MB4XDTI0MDQxNjA4NDQwOFoXDTM0MDQxNDA4NDQwOFowXDELMAkGA1UEBhMCQ0gx
DzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25h
clNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAj644FkWGShwHWckdhjAIWQBUwm6aL1sBRtKL56RywYXO
57eQnnx1d/lWmKZLKG+9PBxpsbDF2OPPtp0je3l38VR1KIuLpviRayQEBC21//Uy
FeFvfwRfn1oN/1oink9bc5egdGi4GDI11iIcdDLR+UytyPVfx+sn7JMsS0pgk85M
OpvEF3CNCN/xPGc8one0d9G8gfgag/gaey9fdO9ooGaiUa3VgQrD55lykOuAncYX
xzpY3Iyr/E2kGY+9SdoyGm+vkh2PkSn37XYQItzqURJBNBrT2DAAy4kfb/1g91+k
zcg0/q2/QZA6uDBtexiZq3+vQok4xxQ30K5swEOQQZQUxn3e586CNd11oWLDOXJ9
EUCLdiklct82/vTQ7CYnqTMLfmdpoJF1swQuSp0GsXbD8n8y5VSWjLi1anX/g7w5
rJBka7278BujDuPrBVYw/JAOOBECe0KPuO/UvjdwugRy8nl7APSulK1HrELZEkoa
cXuaOm3dbICGf8uY7ZnmeVDdeQ0wAHg9W8KaeQb8nrl/LGRJ2XRQIhatXLymynty
t5r8CA2+xeXrn2lB3q5g0JwPctg3Wfo5nhfHddPkXouKj80XDP7wHMb+iXJ9OMMU
Hez0lHlgUKkKyIu0SLSqdgf1OptshRxUAKxBQgvHK5GaVUm8JvBMLKe30A4vu9sC
AwEAAaN1MHMwCwYDVR0PBAQDAgG2MBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1Ud
DgQWBBQg1d+Dd+fr9FR6dbcShukyg/zANDAfBgNVHSMEGDAWgBQg1d+Dd+fr9FR6
dbcShukyg/zANDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBf
+UMY1Ap+EBnVpppyJHmp//D0sJ/ygZ8fiyBmbO4/4XnH3gIl0Y4pR6FXOzU4cDD5
tYD8fPvqlQPdMCwpk/gTH6drg3fdiluAzIO5gFOPg3LYu3Eb9eU/lsickkTCB21F
VwxvHzw4aEirSMoQCA1uCBxzt1f3bEo3DejvBnTtfyAsi/PhLeJ8vi5mXMEI3HiT
yPgHC8lWxRS9i4q4iMGuN5td4ICxLMwabP/K3VKN8mllxvJ85/aMuPhbuylgA8G/
eogNqxG7bwVROR9ua/RLVFsnx/B9awo0ERXBPaFjZlPXr2aBX4rLpu6Sr9ybuDPX
jS1NxEmtNUHFqwMCtpROgDpyxT9XCPlXl9bZFo3Gesv+PfTJnOqH8AWqWG95GM66
QcOQVX6pkD6a9EjjdWN8tBOOiZl7XpG7NyKqNeO/u/fLMCLDMo35Rk9o6FWHE1RK
PMd9sBv9yTbZ1JfZTOBdjWCi7T255gG9TnCsK+LvgGj/uDQK56uNpLh6e+0dbFk1
2XrK3zFAHwoyDpXwRqRRFIgG6/UBGCyCKqR6xDl2/c5J5UVy4ABQq61aI/xuN0xS
mw49bQbwyRmcmFKp8+5RswLx0NrBCY7WPFK+Aq2Fj37c0FpdkZc6BOFgXBAsgK6Y
jTpeIPuigO5pOXs0L8iwGKDkHAoIZWsbQxQNfps3cA==
-----END CERTIFICATE-----

+ 52
- 0
sonar-scanner-engine/src/test/resources/ssl/ca.key Ver fichero

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
+iNXE0r4lx4TmqctoBApwtiahBnPfg==
-----END PRIVATE KEY-----

BIN
sonar-scanner-engine/src/test/resources/ssl/client-truststore.p12 Ver fichero


+ 29
- 0
sonar-scanner-engine/src/test/resources/ssl/client.csr Ver fichero

@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIE9TCCAt0CAQAwXzELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0G
A1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTEVMBMGA1UEAwwM
SnVsaWVuIEhlbnJ5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1d3G
hgrgkmtWl2rMZBMWTUIONuc7nDRG+DmvsOesTTx9AjCO0Wn3q+4Zj3LLQiq9j/N0
goI4BrBluq4Nz6d08y0O5zOPJg9qCQpPlEWXSbrK39OMjRHJn+WmKnHF59EAo0Se
8upR5yF1xBfmb/DGBm30eWnZDf9XKXDqZV+GgT/d/3+569kuJGJEcjuj+vrNM3z/
WXSUS7mNAaDqUApqAZ4/kQa0fybGpC2MwfEzESS+R2RHyPQy1HVXsqkrTnxPSWZO
DC20uWXDk1IlCCgqzKgkbjTvFMsn9GWwaYjcZQXe2b/pgWWMRc31hp6sjUO9EYOn
Y7KLbVsJ558LlzK+8t45kpvPp2OHTPjs+ledSr+m0LAVFn6uXMOCudpo41yHggol
Ep4OpxytkvDfozs9teHbh0NooWnwo65+h7YsEB9CGzG9tIeWZdu51KlPPAYpNafT
9gUR4mDT4a20vGzHb3KtYmPQp6MQc/WwBajW7lE0jFTkukCNhBgL8csEZLgusYui
36mnct3VMPaQXgGQJaR5//Zmp09aTwB9ILl7bzHAG3i5Um3OdNtTa3VW/N1U9lrT
sNrRz2IfEWAwWFhK/oTcyhBx0kIfW5ju2cWbD76Yfq27uiHgYKTPJI/kN/8Vursx
ex7rFKiU+hIvFbB61mqBN9OG7TKjWnJF8oa9GjkCAwEAAaBRME8GCSqGSIb3DQEJ
DjFCMEAwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwEwYDVR0lBAwwCgYIKwYBBQUH
AwIwEQYJYIZIAYb4QgEBBAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQATY8bk0rcI
lwMw9FAYOTrQy4ZVuSEymby01ebjJ6I/1s+UQa6TI9pDigvomy+CgUWdXhquzQ8B
dnOJGibTc3H2iMD5sb0+QhqhJtmCASup9ar1+JQTBEfcorVo2/8qkoEg5oDP+VEI
oHiVzK5LRxfXwHiAoZURi/VJjp+0JJYaCBKrb8hVoGVStTG6qNKFi685v6KhVmM8
Ng3JzuwC15e6LQ9Nr0xULaGcOZR0N/wkUl+2ntXDLIiVORyXmv4lY/Jp7bHhH7qr
SecpEROzOZzIdzG4Iyy5Mj/DIPqKk55f5HuVXyhRiar89TBKrIwBVytlKPOZH4Ot
rUTbKnz6OqBvXPFV6+YNyQb4LesU0CSWUVzFtYygBk1fcr+l7Bluxk29FTDf9iZL
sc5v6fvrBhJmKHBQVTR1h9MvRpgpuImvRVdmOKNDXY7KpC7kB+DjKFEoQEtqJG0D
53dxbkH7oaz57oSXPYjsAlezsDbAlWRFPD4gA+TqAJ0BkzoTggmctgJ8EpVkgzvG
7iQAV+KN7OMClft4AQQO4224fSi9t8PSy+pWBc5wVDWLmnWxIkBHyBs+JgYJZ9pX
+Xo+S+FDxIK6ALirSeJlTDvIKgjgtLzXA6a+BNMNyQPKZHIjRqU36L/7fYyZnmTg
aoBsmYBIm3xEMVi0VDBNFRPmLugPM2v9GQ==
-----END CERTIFICATE REQUEST-----

+ 52
- 0
sonar-scanner-engine/src/test/resources/ssl/client.key Ver fichero

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
vsseryZb70+/NjZqWcSTcuFhooe9+Q==
-----END PRIVATE KEY-----

BIN
sonar-scanner-engine/src/test/resources/ssl/client.p12 Ver fichero


+ 31
- 0
sonar-scanner-engine/src/test/resources/ssl/client.pem Ver fichero

@@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFRDCCAywCFF+7ThfA7f+eATq97zVNq8PRccLPMA0GCSqGSIb3DQEBCwUAMF4x
CzAJBgNVBAYTAkNIMQ8wDQYDVQQIDAZHZW5ldmExDzANBgNVBAcMBkdlbmV2YTEX
MBUGA1UECgwOU29uYXJTb3VyY2UgU0ExFDASBgNVBAMMC1NvbmFyU291cmNlMB4X
DTI0MDQxNjA5MzYwM1oXDTM0MDQxNDA5MzYwM1owXzELMAkGA1UEBhMCQ0gxDzAN
BgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25hclNv
dXJjZSBTQTEVMBMGA1UEAwwMSnVsaWVuIEhlbnJ5MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA1d3GhgrgkmtWl2rMZBMWTUIONuc7nDRG+DmvsOesTTx9
AjCO0Wn3q+4Zj3LLQiq9j/N0goI4BrBluq4Nz6d08y0O5zOPJg9qCQpPlEWXSbrK
39OMjRHJn+WmKnHF59EAo0Se8upR5yF1xBfmb/DGBm30eWnZDf9XKXDqZV+GgT/d
/3+569kuJGJEcjuj+vrNM3z/WXSUS7mNAaDqUApqAZ4/kQa0fybGpC2MwfEzESS+
R2RHyPQy1HVXsqkrTnxPSWZODC20uWXDk1IlCCgqzKgkbjTvFMsn9GWwaYjcZQXe
2b/pgWWMRc31hp6sjUO9EYOnY7KLbVsJ558LlzK+8t45kpvPp2OHTPjs+ledSr+m
0LAVFn6uXMOCudpo41yHggolEp4OpxytkvDfozs9teHbh0NooWnwo65+h7YsEB9C
GzG9tIeWZdu51KlPPAYpNafT9gUR4mDT4a20vGzHb3KtYmPQp6MQc/WwBajW7lE0
jFTkukCNhBgL8csEZLgusYui36mnct3VMPaQXgGQJaR5//Zmp09aTwB9ILl7bzHA
G3i5Um3OdNtTa3VW/N1U9lrTsNrRz2IfEWAwWFhK/oTcyhBx0kIfW5ju2cWbD76Y
fq27uiHgYKTPJI/kN/8Vursxex7rFKiU+hIvFbB61mqBN9OG7TKjWnJF8oa9GjkC
AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAfmCoELV+5Nk4Cz0FP6xT2FP1YNXJJR2u
qNilLDL6TqD/ONgPdmbMgwx5dNcSxOL8Fg2dUENgjwyl96WAfnFy+F6V9PFZmVk3
BE0CWBMMb37Vr3gptyBdV9sOtgM4lIUwu+PYh4ykvk+cd6l7GfWBIDKpKA0mXJyY
V3IkaFuxboOYE8CptNMH42vYPfAxQi+hFLzj97+Jhb/Ycv7gZHEvjt+EtSMmPTta
OtOVMV0c8rFgvFraFloX0idEswBRzt+LG1F10zXQwG3aRLqde9t/SiQuTL1pctLt
VMidqUSHfVYJOiqKnwZ+SWq75VRPBRSPowriZG2bOuLpkAYCEOU5+tYh3+pcGptq
dl8Zcxrhmc9/LcDjPNtvxSNKQoM5rCVW2BRdqv0E0uxujlxtiWfQ3UnjfLdJgW1Y
wF5kMSn6A7Nt+SkLMfao4Y1Toj/EywPvpnf8Dw4DwYd7UtKXKdY7i1mBAmqmSemN
M2VLsP+O8+njHP+qtXPBsU8PBrgGpDh4zgmEGUoAPDea9uhX7F0ArSxZhOUpDGIj
8HNBN52R+lZpjIZeIQlS2sIUGVm7XQY8I/11u26VgiKyPJxUOp19RjLzulk0v7yb
MOGdW8OcQ03On9ucoTC2AbknqxX5/3fmH3f5ztbcGtZ0lE5LihySNyk7wLgkqHUA
8JnfYM95xuk=
-----END CERTIFICATE-----

+ 33
- 0
sonar-scanner-engine/src/test/resources/ssl/openssl-client-auth.conf Ver fichero

@@ -0,0 +1,33 @@
HOME = .
RANDFILE = $ENV::HOME/.rnd

[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = client_extensions

[ req_distinguished_name ]
countryName = Country Name (2-letter code)
countryName_default = CH
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Geneva
localityName = Locality (e.g. city name)
localityName_default = Geneva
organizationName = Organization (e.g. company name)
organizationName_default = SonarSource SA
commonName = Common Name (your.domain.com)
commonName_default = Julien Henry

[ client_extensions ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
nsCertType = client

[ ca_extensions ]
basicConstraints = CA:FALSE
keyUsage = keyEncipherment, dataEncipherment, keyCertSign, cRLSign, digitalSignature
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true

+ 34
- 0
sonar-scanner-engine/src/test/resources/ssl/openssl.conf Ver fichero

@@ -0,0 +1,34 @@
HOME = .

[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = req_extensions

[ req_distinguished_name ]
countryName = Country Name (2-letter code)
countryName_default = CH
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Geneva
localityName = Locality (e.g. city name)
localityName_default = Geneva
organizationName = Organization (e.g. company name)
organizationName_default = SonarSource SA
commonName = Common Name (your.domain.com)
commonName_default = localhost

[ req_extensions ]
subjectAltName = @alt_names
keyUsage = keyEncipherment, dataEncipherment, digitalSignature
extendedKeyUsage = serverAuth

[ ca_extensions ]
basicConstraints = CA:FALSE
keyUsage = keyEncipherment, dataEncipherment, keyCertSign, cRLSign, digitalSignature
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints = critical, CA:true

[ alt_names ]
DNS.1 = localhost

BIN
sonar-scanner-engine/src/test/resources/ssl/server-with-client-ca.p12 Ver fichero


+ 29
- 0
sonar-scanner-engine/src/test/resources/ssl/server.csr Ver fichero

@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIE6jCCAtICAQAwXDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0G
A1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTESMBAGA1UEAwwJ
bG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArRRQF25E
5NCgXdoEBU2SWyAoyOWMGVT1Ioltnr3sJP6LMjjfozK5YgaRn504291lwlG+k6tv
zTSR9HB8q3ITa8AdnwMiL7jzbveYKWIlLQ7kdHKXWbiaIjTaZCyfnWUlDFIuR7BH
wOXVwyLrBQfhoyDVaaoyowQEsUro3okIR/kBsqM+KH8bcdl06DMMppZ8Qy1DYvPo
dhnNRyOSSpfbIoodE1fju+5U0OKzvGIc9WpG5pKIysaW3whOa/ieb02SXrgoiHnY
PpmmGzm4u/Wn8jGwhYQJSQT10yjMacGHwmBEq7FUr854cVd+eend056P6pwUukdN
eVHCFjYRkmWCNzIxV+sS9PPtDs77/bLFIItrnBMHVsId38tPoru/z1S1p2dzCX3N
q09aJFF/vH2u9Sg5aerHJ7xnRroR1jIrAZtcjBkJHEiTlG+WaavP4j6oym+lvHvg
HHL3Qwhh8emg0JiLYExVV7ma70aRDh8yoQtSzAUDMVfhVPKd92MS+7DC2pv2KviU
NKqbHDFadl01JN3t+17/gstUNSk1jpoUfUhKBeUQxVEdVUy2p0HeD/TYpRvF2FEs
Wneq3+ZbnRp17I/uEQOck0LP2tkzAd4tmRgH+95yyB8MgbAfvyKWkB4+3BhtdfoY
De1asqR6z43mejDHHqgBXn+u3UKjPypKfPECAwEAAaBJMEcGCSqGSIb3DQEJDjE6
MDgwFAYDVR0RBA0wC4IJbG9jYWxob3N0MAsGA1UdDwQEAwIEsDATBgNVHSUEDDAK
BggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAWLCYfUyCFT1bOafpcrjdpHaE
Z+DgrFniECq/gbx3DYdYHCxmfXVKuLGby2uNoujPPW4pIRzfbFjijoHN50uF8tnX
qtpHlEACoFnpMUOfTyP9FnjT33VdSNoMdXv1xfRtIZE+WUE9CemYFBChGobo861a
au3QYyyHiSTeAsE/cNOBoaNwvUieTRjX8NAq69vkkmFaEIstKingEt3HVMD3yZPu
qIMRuzrH63H0YOf9BsHieRTbV4h8FzmJyuaHMGpmBbP5zM3ynvoBlCki3sLYQQ78
jrfxLHpco40Re4/Er9qra5LtEYBZEaooTHd1oPxyIxGiaw6pLLi/VlWy64L9Bt9w
Ta2suIHoUElL7F+QXkh6+16ljdghjJCvhIrFOdf7hzcG+x3u4H24Nm5bxYXiSpQD
DAkj1mzuO49iqRhytEh6LMAXtMp8ntX79gYLkyaa0TRChYqwYNNlTzAmsG4vO7Hy
+dOc1hF5RIQd3TL7jmGDLlI+Y4DXrPMVa/6edimIcBFCgdYau70vBK9P5z+5HroL
AZS2/kxTT3eaWBF77mCfyqXq/qfiUaQk1d7qszfJxpdbar4tJqOPXxJa8mK1AIuI
tezgiHDB+4mM9XhVF2tdIekLfvVuRIKOA+9xbPtnrYRngH0VLcPOYlt+eC95N45D
cMyWA9ULzIcwEeBjYSE=
-----END CERTIFICATE REQUEST-----

+ 52
- 0
sonar-scanner-engine/src/test/resources/ssl/server.key Ver fichero

@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCtFFAXbkTk0KBd
2gQFTZJbICjI5YwZVPUiiW2evewk/osyON+jMrliBpGfnTjb3WXCUb6Tq2/NNJH0
cHyrchNrwB2fAyIvuPNu95gpYiUtDuR0cpdZuJoiNNpkLJ+dZSUMUi5HsEfA5dXD
IusFB+GjINVpqjKjBASxSujeiQhH+QGyoz4ofxtx2XToMwymlnxDLUNi8+h2Gc1H
I5JKl9siih0TV+O77lTQ4rO8Yhz1akbmkojKxpbfCE5r+J5vTZJeuCiIedg+maYb
Obi79afyMbCFhAlJBPXTKMxpwYfCYESrsVSvznhxV3556d3Tno/qnBS6R015UcIW
NhGSZYI3MjFX6xL08+0Ozvv9ssUgi2ucEwdWwh3fy0+iu7/PVLWnZ3MJfc2rT1ok
UX+8fa71KDlp6scnvGdGuhHWMisBm1yMGQkcSJOUb5Zpq8/iPqjKb6W8e+AccvdD
CGHx6aDQmItgTFVXuZrvRpEOHzKhC1LMBQMxV+FU8p33YxL7sMLam/Yq+JQ0qpsc
MVp2XTUk3e37Xv+Cy1Q1KTWOmhR9SEoF5RDFUR1VTLanQd4P9NilG8XYUSxad6rf
5ludGnXsj+4RA5yTQs/a2TMB3i2ZGAf73nLIHwyBsB+/IpaQHj7cGG11+hgN7Vqy
pHrPjeZ6MMceqAFef67dQqM/Kkp88QIDAQABAoICACd11N/OsG7hoNpc6SNDYQ2d
GqdY7HTfEYeHASLaxrLVhOtVm6lD2I/AkyVqrUq1YqynwfU9dh85L9ik58uX1dUw
ZyB4kKwENR4U3ZB706F+/neNI7QdOij3113U7awvIf/54ZrPFkDktbSIasBKIHe2
demiF+riMOax/z8zS1vLagduH+8QMbPmgfipoOX/M8QGFxHBrbt1XP+t3L3ceuXY
StI87MtNRnGcaiGWVedfDFynxn/CwKWHaYfE1mxmaWtmfblF3FdDZSNaaOOTma+G
hCogpRRMiPZUXCx1ZuwaUjW516bAgmXG7qtBdmV3xnSVEsW4mXGCQieZuq5frczi
3+dRbYC4VkgmzW63XrgH0FuNFJ4sMdB8QeYRl3FDP+wMhL2ObvABmpvReASWiVON
R+x9ssYLQUdS/Znr8M8KWDvZ+zURyzbz9kdkPJseV/Xeekt76nbNRd0jsmjt6n1w
eQa2hFocN7CgM1pg8EIW/xLNbkokYtAZAGnHgTuLlFMJSgMj0u8+NyZsIHrbW36X
7XBWaMM145G3PpnycoYZ3UAyYJTGPFEN5FImoZwJAIS2lKpoQN2G5T+iPeFQDmvX
pO/Y21tj2wjl6y1mT+GvurnF980+aS2Ibu0cN8BTxpPAt1f/oOUKKv4cPYGfsXq4
TKtoqPkPGhSSIyuyfHKlAoIBAQC/jHbybKXmodEc7S9AS5p+O7WTT0jQO6VMK/cp
woEcZlnDDqowkiaX+Wn7ybFDAkZwioCtLnRx8IeMmDLJpfRyme/wNEDY62pEncHY
MZwEEUT1GtBYNAmkKLWO+ThfGiUGHnMw2F1nXbZRzNZLpt7U6/of3ydUVcWDwMus
29gLbRgPQ2DMdrPLsUk3T82TaNv0pdCvfZjDW08rjS4/mBgiIdnXT4q5P2wBTmFz
1tfXMtXp5puw3qYbPGiX3PYozIWMXzZnmbgCijUCNrBxbxA9YKjElkIfTur3y/W0
Z7Li2pROQyaQ6gGixf876LAl27xCRpGzoBlTBYMnlciI1Yc9AoIBAQDnUPG4zTLZ
YVoBt5tP7lXRE8LsQUC30LK6Rszu3TRnzvQP7qYVo7/RC7J3DhyghSEFEwdQ27Vb
9buAkowVwhWf6rpygwUChaiT7xOK9NaIvCybBSa9ZpPVe07mAuVNYzp2qE6DXrW3
/gvm5twr7Noc/YS210xa2SB5Gun7TghaAb/mjGDZSa0JnqVDz3BOo3YmBdgHcAic
fsRRmK3YmYDraxSZuD+cs30jPZw60c7RSdN59kf7i8cQ6G2gZCVo1hVPDys95jbN
jtlu1OOdLv83iMEw/W2rpMOCMy5+Dhielx5Ra/K2T6OZC8woXcza8JFy9xDiF6DC
Okqm7rZOzcfFAoIBAQCYuogRDd5OAZI5zUiSrHWX10YVGe+F0TkgfiHKE0NdAKLr
q2K57Z6GKKF/2LbVJhhCHb0x2MuSGeYKjURZklBRnDo7PX7DNxn5cgwgtJWgjKB+
Co469esGEEuLn116Pt9sfJT+SlZXV9pKaNgpY/lirnE2PnkefnFJd00vG++sVKUN
bnzdKnx7mnU1fBT/R2myLRAzDSLkCYcbw6svm7cKaBFI4yxKPq6AcB21/oUFGoyD
vpM/OJgbOVRwWgeQSlrlrPk1K9UTeV2A0VhoadT6C3slnGVGj2c8g0z7Nn/k78G2
kUZL37nELrku7H6fARCfi6MbJTlsAAYuZviJWjBFAoIBAFTkjBH6nRLSe6ntrH5l
RfF5gywZtpq/aRicK1HutPD0LvY565I9ioQ5+sFe2HrA4SFvnlu6hpC9WpcRMYA6
vpz2FH86PnhyfS/tqgpxWNrN1MD/3vvbzZ2np4kavvTr2eT6V/Y2qBJilhOj3mHw
hwvkrvQ7h7Y/wX8wtXaZaM8/nSILmu+j7nF9W8HLO7hgnVfPBT2VjFOC4qHfms3H
aFz9642O5SmpZd+tGM0teu2sXoSAMmLLJb+6zaDzoBcdmqxtML2C49IE/x+B2hcx
zFChS+Wi3MEFswrxpbp1ieuKIoJXT7hA+hWNEtwtsKUZbQf4TKXtbf5aTlN9gELj
mtECggEAC0x8yvZefPbczQ10qENLmnfYrSGjiuXN794Z3y4DwO8R0xnfdYy8zibQ
QF3+3LqhKVtu7DVF1ZJvisl+WntURA8oUp8VUeMwLymf+LBSnXfD7FhtWxkk/v7M
KqHPUFzIi/fxKUoiVxgS2QDjCSU9zIzDFD/Uz4KMDq70wmRPf621tSfxFMEQ//Cd
DjRhrqu1Vs8Gf29QXjOfrz9DnRI4IzXRr0+uKEd4H8caMK3uGFFEUnDBU7amw5Tz
SVf9+1Uhseo+hLJlproKDB6TIkx06sv7bMpfpZiMEkT2zzQh59WFLeX57FIeUiBu
9uPzaGE09HfpBsTJtUTjU0mClVlOCw==
-----END PRIVATE KEY-----

BIN
sonar-scanner-engine/src/test/resources/ssl/server.p12 Ver fichero


+ 33
- 0
sonar-scanner-engine/src/test/resources/ssl/server.pem Ver fichero

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFtjCCA56gAwIBAgIULroxFuPWyNOiQtAVPS/XFFMXp6owDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCQ0gxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMRcwFQYDVQQKDA5Tb25hclNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0
MB4XDTI0MDQxNjA4NDUyMVoXDTM0MDQxNDA4NDUyMVowXDELMAkGA1UEBhMCQ0gx
DzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2VuZXZhMRcwFQYDVQQKDA5Tb25h
clNvdXJjZSBTQTESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEArRRQF25E5NCgXdoEBU2SWyAoyOWMGVT1Ioltnr3sJP6L
MjjfozK5YgaRn504291lwlG+k6tvzTSR9HB8q3ITa8AdnwMiL7jzbveYKWIlLQ7k
dHKXWbiaIjTaZCyfnWUlDFIuR7BHwOXVwyLrBQfhoyDVaaoyowQEsUro3okIR/kB
sqM+KH8bcdl06DMMppZ8Qy1DYvPodhnNRyOSSpfbIoodE1fju+5U0OKzvGIc9WpG
5pKIysaW3whOa/ieb02SXrgoiHnYPpmmGzm4u/Wn8jGwhYQJSQT10yjMacGHwmBE
q7FUr854cVd+eend056P6pwUukdNeVHCFjYRkmWCNzIxV+sS9PPtDs77/bLFIItr
nBMHVsId38tPoru/z1S1p2dzCX3Nq09aJFF/vH2u9Sg5aerHJ7xnRroR1jIrAZtc
jBkJHEiTlG+WaavP4j6oym+lvHvgHHL3Qwhh8emg0JiLYExVV7ma70aRDh8yoQtS
zAUDMVfhVPKd92MS+7DC2pv2KviUNKqbHDFadl01JN3t+17/gstUNSk1jpoUfUhK
BeUQxVEdVUy2p0HeD/TYpRvF2FEsWneq3+ZbnRp17I/uEQOck0LP2tkzAd4tmRgH
+95yyB8MgbAfvyKWkB4+3BhtdfoYDe1asqR6z43mejDHHqgBXn+u3UKjPypKfPEC
AwEAAaNwMG4wHwYDVR0jBBgwFoAUINXfg3fn6/RUenW3EobpMoP8wDQwCQYDVR0T
BAIwADALBgNVHQ8EBAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MB0GA1UdDgQW
BBRX4bsny+8GQcFpM10jtAfFxzNxzzANBgkqhkiG9w0BAQsFAAOCAgEAa+Myw6li
Fme95cPpINTite/9LXk+TlHHnXiV5Z+Um3NTLSllX3zPuRFiOE71OKFrWQPqH2N/
85l6h19G9xQsaqkkVFyQENkNzykZpJL/jU4+wgRtwcEDkaRGGURZacz3vfLTc1HX
tPDNv/JsZ5HE2d7cF5YhN4UahtxS2lvarrSujaOBpFZTT6PbEYX9EnwCdapORHOh
wKMc3OGGOiGWvRlVaWu/Huq2HvXXcK0pmaYWWKX3u21evthSYOu9U4Rk0z1y7m3/
CIYaIrvSbkzq2KKXMn7lr26bv2cthAQrPAjb2ILPUoyzKa3wEK3lkhanM6PN9CMH
y5KRTpqwV45Qr6BAVY1bP67pEkay2T31chIVKds6dkx9b2/bWpW9PWuymsbWX2vO
Q1MiaPkXKSTgCRwQUR0SNbPHw3X+VhrKKJB+beX8Bh2fcKw3jGGM8oHiA1hpdnbg
Y5fW7EupF5gabf2jNB1XJ4gowlpB3nTooKFgbcgsvi68MRdBno2TWUhsZ3zCVyaH
KFdDV0f78Fg7oL79K3kBL/iqr+jsb8sFHKIS4Dyyz2rDJrE0q0xAPes+Bu75R3/5
M/s2H7KuLqLdDYsCsMeMqOVuIcAyPp2MFWInYPyi0zY4fwKwm8f/Kv8Lzb+moxqI
Fct6d1S08JAosVnZcP2P7Yz+TbmDRtsqCgk=
-----END CERTIFICATE-----

+ 7
- 0
sonar-scanner-engine/src/test/resources/ssl/v3.ext Ver fichero

@@ -0,0 +1,7 @@
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

Cargando…
Cancelar
Guardar