diff --git a/lib/src/main/java/org/sonarsource/scanner/lib/ScannerProperties.java b/lib/src/main/java/org/sonarsource/scanner/lib/ScannerProperties.java index 0f758e20..26217100 100644 --- a/lib/src/main/java/org/sonarsource/scanner/lib/ScannerProperties.java +++ b/lib/src/main/java/org/sonarsource/scanner/lib/ScannerProperties.java @@ -84,7 +84,7 @@ private ScannerProperties() { public static final String SONAR_SCANNER_KEYSTORE_PASSWORD = "sonar.scanner.keystorePassword"; public static final String SONAR_SCANNER_TRUSTSTORE_PATH = "sonar.scanner.truststorePath"; public static final String SONAR_SCANNER_TRUSTSTORE_PASSWORD = "sonar.scanner.truststorePassword"; - + public static final String SONAR_SCANNER_SKIP_SYSTEM_TRUSTSTORE = "sonar.scanner.skipSystemTruststore"; /** * Skip analysis. */ diff --git a/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java b/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java index 0f240070..bb6e8c20 100644 --- a/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java +++ b/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java @@ -39,6 +39,7 @@ import static java.lang.Integer.parseInt; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; +import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_SKIP_SYSTEM_TRUSTSTORE; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_CONNECT_TIMEOUT; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PASSWORD; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PATH; @@ -79,6 +80,7 @@ public class HttpConfig { private final String proxyUser; private final String proxyPassword; private final String userAgent; + private final boolean skipSystemTrustMaterial; public HttpConfig(Map bootstrapProperties, Path sonarUserHome) { this.webApiBaseUrl = StringUtils.removeEnd(bootstrapProperties.get(ScannerProperties.HOST_URL), "/"); @@ -94,6 +96,7 @@ public HttpConfig(Map bootstrapProperties, Path sonarUserHome) { this.proxy = loadProxy(bootstrapProperties); this.proxyUser = loadProxyUser(bootstrapProperties); this.proxyPassword = loadProxyPassword(bootstrapProperties); + this.skipSystemTrustMaterial = Boolean.parseBoolean(defaultIfBlank(bootstrapProperties.get(SONAR_SCANNER_SKIP_SYSTEM_TRUSTSTORE), "false")); } private static String loadProxyPassword(Map bootstrapProperties) { @@ -249,4 +252,8 @@ public String getProxyUser() { public String getProxyPassword() { return proxyPassword; } + + public boolean skipSystemTruststore() { + return skipSystemTrustMaterial; + } } diff --git a/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactory.java b/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactory.java index 269182f3..c2b5ae0f 100644 --- a/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactory.java +++ b/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactory.java @@ -50,6 +50,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_SKIP_SYSTEM_TRUSTSTORE; public class OkHttpClientFactory { @@ -72,7 +73,7 @@ private OkHttpClientFactory() { static OkHttpClient create(HttpConfig httpConfig) { - var sslContext = configureSsl(httpConfig.getSslConfig()); + var sslContext = configureSsl(httpConfig.getSslConfig(), httpConfig.skipSystemTruststore()); OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder() .connectTimeout(httpConfig.getConnectTimeout().toMillis(), TimeUnit.MILLISECONDS) @@ -112,10 +113,14 @@ static OkHttpClient create(HttpConfig httpConfig) { return okHttpClientBuilder.build(); } - private static SSLFactory configureSsl(SslConfig sslConfig) { + private static SSLFactory configureSsl(SslConfig sslConfig, boolean skipSystemTrustMaterial) { var sslFactoryBuilder = SSLFactory.builder() - .withDefaultTrustMaterial() - .withSystemTrustMaterial(); + .withDefaultTrustMaterial(); + if (!skipSystemTrustMaterial) { + LOG.debug("Loading OS trusted SSL certificates..."); + LOG.debug("This operation might be slow or even get stuck. You can skip it by passing the scanner property '{}=true'", SONAR_SCANNER_SKIP_SYSTEM_TRUSTSTORE); + sslFactoryBuilder.withSystemTrustMaterial(); + } if (System.getProperties().containsKey("javax.net.ssl.keyStore")) { sslFactoryBuilder.withSystemPropertyDerivedIdentityMaterial(); } diff --git a/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactoryTest.java b/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactoryTest.java index f95ddad6..6fae7bd9 100644 --- a/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactoryTest.java +++ b/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/OkHttpClientFactoryTest.java @@ -45,6 +45,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junitpioneer.jupiter.RestoreSystemProperties; +import org.slf4j.event.Level; +import testutils.LogTester; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; @@ -68,6 +70,9 @@ class OkHttpClientFactoryTest { private final Map bootstrapProperties = new HashMap<>(); + @RegisterExtension + private LogTester logTester = new LogTester(); + @TempDir private Path sonarUserHomeDir; private Path sonarUserHome; @@ -131,6 +136,25 @@ void it_should_fail_if_invalid_keystore_password(String keystore, @Nullable Stri } } + @Test + void should_load_os_certificates_by_default() { + logTester.setLevel(Level.DEBUG); + + OkHttpClientFactory.create(new HttpConfig(bootstrapProperties, sonarUserHome)); + + assertThat(logTester.logs(Level.DEBUG)).contains("Loading OS trusted SSL certificates..."); + } + + @Test + void should_skip_load_of_os_certificates_if_props_set() { + logTester.setLevel(Level.DEBUG); + bootstrapProperties.put("sonar.scanner.skipSystemTruststore", "true"); + + OkHttpClientFactory.create(new HttpConfig(bootstrapProperties, sonarUserHome)); + + assertThat(logTester.logs(Level.DEBUG)).doesNotContain("Loading OS trusted SSL certificates..."); + } + @Nested // Workaround until we move to Java 17+ and can make Wiremock extension static @TestInstance(TestInstance.Lifecycle.PER_CLASS)