Skip to content

Commit 8e5ebc0

Browse files
committed
SCANJLIB-259 Restore default truststore location
1 parent b7a902f commit 8e5ebc0

File tree

11 files changed

+292
-140
lines changed

11 files changed

+292
-140
lines changed

its/it-tests/src/test/java/com/sonar/scanner/lib/it/SSLTest.java

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
2828
import com.tngtech.java.junit.dataprovider.UseDataProvider;
2929
import java.net.InetAddress;
30+
import java.nio.file.Files;
3031
import java.nio.file.Path;
3132
import java.nio.file.Paths;
3233
import java.util.HashMap;
@@ -49,7 +50,9 @@
4950
import org.junit.After;
5051
import org.junit.Before;
5152
import org.junit.ClassRule;
53+
import org.junit.Rule;
5254
import org.junit.Test;
55+
import org.junit.rules.TemporaryFolder;
5356
import org.junit.runner.RunWith;
5457

5558
import static org.assertj.core.api.Assertions.assertThat;
@@ -84,6 +87,9 @@ public class SSLTest {
8487
@ClassRule
8588
public static final OrchestratorRule ORCHESTRATOR = ScannerJavaLibraryTestSuite.ORCHESTRATOR;
8689

90+
@Rule
91+
public TemporaryFolder scannerHome = new TemporaryFolder();
92+
8793
@Before
8894
public void deleteData() {
8995
ScannerJavaLibraryTestSuite.resetData(ORCHESTRATOR);
@@ -234,7 +240,7 @@ private static Path project(String projectName) {
234240

235241
@Test
236242
@UseDataProvider("variousClientTrustStores")
237-
public void simple_analysis_with_server_certificate(String clientTrustStore, String keyStorePassword, boolean useJavaSslProperties) throws Exception {
243+
public void simple_analysis_with_server_certificate(String clientTrustStore, String keyStorePassword, TrustStoreConfigurationMethod configMethod) throws Exception {
238244
assumeTrue("Support for PKCS12 keystore generated by openssl was added in SQ 10.7", !clientTrustStore.endsWith("-openssl.p12") ||
239245
ORCHESTRATOR.getServer().version().isGreaterThanOrEquals(10, 7));
240246
startSSLTransparentReverseProxy(false);
@@ -248,12 +254,23 @@ public void simple_analysis_with_server_certificate(String clientTrustStore, Str
248254
assertThat(clientTruststore).exists();
249255

250256
Map<String, String> params = new HashMap<>();
251-
if (useJavaSslProperties) {
252-
params.put("javax.net.ssl.trustStore", clientTruststore.toString());
253-
params.put("javax.net.ssl.trustStorePassword", keyStorePassword);
254-
} else {
255-
params.put("sonar.scanner.truststorePath", clientTruststore.toString());
256-
params.put("sonar.scanner.truststorePassword", keyStorePassword);
257+
switch (configMethod) {
258+
case JVM_PROPERTIES:
259+
params.put("javax.net.ssl.trustStore", clientTruststore.toString());
260+
params.put("javax.net.ssl.trustStorePassword", keyStorePassword);
261+
break;
262+
case SCANNER_PROPERTIES:
263+
params.put("sonar.scanner.truststorePath", clientTruststore.toString());
264+
params.put("sonar.scanner.truststorePassword", keyStorePassword);
265+
break;
266+
case DEFAULT_LOCATION:
267+
var scannerHomeDir = scannerHome.newFolder();
268+
params.put("sonar.userHome", scannerHomeDir.getAbsolutePath());
269+
var defaultTruststore = scannerHomeDir.toPath().resolve("ssl/truststore.p12");
270+
Files.createDirectories(defaultTruststore.getParent());
271+
Files.copy(clientTruststore, defaultTruststore);
272+
params.put("sonar.scanner.truststorePassword", keyStorePassword);
273+
break;
257274
}
258275

259276
buildResult = scanner.executeSimpleProject(project("js-sample"), "https://localhost:" + httpsPort, params, Map.of());
@@ -263,13 +280,29 @@ public void simple_analysis_with_server_certificate(String clientTrustStore, Str
263280

264281
@DataProvider()
265282
public static Object[][] variousClientTrustStores() {
266-
return new Object[][]{
267-
{KEYSTORE_CLIENT_WITH_CA_KEYTOOL, CLIENT_WITH_CA_KEYSTORE_PASSWORD, true},
268-
{KEYSTORE_CLIENT_WITH_CA_OPENSSL, CLIENT_WITH_CA_KEYSTORE_PASSWORD, false},
269-
{KEYSTORE_CLIENT_WITH_CERTIFICATE_KEYTOOL, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, true},
270-
{KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, false},
271-
{KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL_JDKTRUST, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, false},
272-
{KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL_JDKTRUST, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD, true}
273-
};
283+
Map<String, String> keystoresAndPasswords = Map.of(
284+
KEYSTORE_CLIENT_WITH_CA_KEYTOOL, CLIENT_WITH_CA_KEYSTORE_PASSWORD,
285+
KEYSTORE_CLIENT_WITH_CA_OPENSSL, CLIENT_WITH_CA_KEYSTORE_PASSWORD,
286+
KEYSTORE_CLIENT_WITH_CERTIFICATE_KEYTOOL, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD,
287+
KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD,
288+
KEYSTORE_CLIENT_WITH_CERTIFICATE_OPENSSL_JDKTRUST, CLIENT_WITH_CERTIFICATE_KEYSTORE_PASSWORD);
289+
290+
Object[][] result = new Object[keystoresAndPasswords.size() * TrustStoreConfigurationMethod.values().length][3];
291+
int index = 0;
292+
for (TrustStoreConfigurationMethod method : TrustStoreConfigurationMethod.values()) {
293+
for (Map.Entry<String, String> keystoreAndPassword : keystoresAndPasswords.entrySet()) {
294+
result[index][0] = keystoreAndPassword.getKey();
295+
result[index][1] = keystoreAndPassword.getValue();
296+
result[index][2] = method;
297+
index++;
298+
}
299+
}
300+
return result;
301+
}
302+
303+
private enum TrustStoreConfigurationMethod {
304+
JVM_PROPERTIES,
305+
SCANNER_PROPERTIES,
306+
DEFAULT_LOCATION
274307
}
275308
}

lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapper.java

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.sonarsource.scanner.lib;
2121

2222
import java.net.InetSocketAddress;
23-
import java.nio.file.Files;
2423
import java.nio.file.Path;
2524
import java.nio.file.Paths;
2625
import java.time.temporal.ChronoUnit;
@@ -53,7 +52,6 @@
5352
import org.sonarsource.scanner.lib.internal.util.System2;
5453
import org.sonarsource.scanner.lib.internal.util.VersionUtils;
5554

56-
import static java.util.Optional.ofNullable;
5755
import static org.sonarsource.scanner.lib.EnvironmentConfig.TOKEN_ENV_VARIABLE;
5856
import static org.sonarsource.scanner.lib.ScannerProperties.SCANNER_ARCH;
5957
import static org.sonarsource.scanner.lib.ScannerProperties.SCANNER_OS;
@@ -63,6 +61,14 @@
6361
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PASSWORD;
6462
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PATH;
6563
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_TOKEN;
64+
import static org.sonarsource.scanner.lib.internal.JvmProperties.HTTPS_PROXY_HOST;
65+
import static org.sonarsource.scanner.lib.internal.JvmProperties.HTTPS_PROXY_PORT;
66+
import static org.sonarsource.scanner.lib.internal.JvmProperties.HTTP_PROXY_HOST;
67+
import static org.sonarsource.scanner.lib.internal.JvmProperties.HTTP_PROXY_PORT;
68+
import static org.sonarsource.scanner.lib.internal.JvmProperties.JAVAX_NET_SSL_KEY_STORE;
69+
import static org.sonarsource.scanner.lib.internal.JvmProperties.JAVAX_NET_SSL_KEY_STORE_PASSWORD;
70+
import static org.sonarsource.scanner.lib.internal.JvmProperties.JAVAX_NET_SSL_TRUST_STORE;
71+
import static org.sonarsource.scanner.lib.internal.JvmProperties.JAVAX_NET_SSL_TRUST_STORE_PASSWORD;
6672

6773
/**
6874
* Entry point to run a Sonar analysis programmatically.
@@ -75,10 +81,6 @@ public class ScannerEngineBootstrapper {
7581
private static final String SONARCLOUD_REST_API = "https://api.sonarcloud.io";
7682
static final String SQ_VERSION_NEW_BOOTSTRAPPING = "10.6";
7783
static final String SQ_VERSION_TOKEN_AUTHENTICATION = "10.0";
78-
private static final String JAVAX_NET_SSL_TRUST_STORE = "javax.net.ssl.trustStore";
79-
private static final String JAVAX_NET_SSL_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword";
80-
private static final String JAVAX_NET_SSL_KEY_STORE = "javax.net.ssl.keyStore";
81-
private static final String JAVAX_NET_SSL_KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword";
8284

8385
private final IsolatedLauncherFactory launcherFactory;
8486
private final ScannerEngineLauncherFactory scannerEngineLauncherFactory;
@@ -124,11 +126,11 @@ public ScannerEngineBootstrapResult bootstrap() {
124126
LOG.debug("Scanner max available memory: {}", FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory()));
125127
}
126128
initBootstrapDefaultValues();
127-
adaptJvmSslPropertiesToScannerProperties(bootstrapProperties, system);
128129
var immutableProperties = Map.copyOf(bootstrapProperties);
130+
var sonarUserHome = resolveSonarUserHome(immutableProperties);
131+
var httpConfig = new HttpConfig(immutableProperties, sonarUserHome, system);
129132
var isSonarCloud = isSonarCloud(immutableProperties);
130133
var isSimulation = immutableProperties.containsKey(InternalProperties.SCANNER_DUMP_TO_FILE);
131-
var sonarUserHome = resolveSonarUserHome(immutableProperties);
132134
var fileCache = FileCache.create(sonarUserHome);
133135

134136
if (isSimulation) {
@@ -138,7 +140,6 @@ public ScannerEngineBootstrapResult bootstrap() {
138140

139141
// No HTTP call should be made before this point
140142
try {
141-
var httpConfig = new HttpConfig(immutableProperties, sonarUserHome);
142143
scannerHttpClient.init(httpConfig);
143144

144145
var serverVersion = !isSonarCloud ? getServerVersion(scannerHttpClient) : null;
@@ -151,7 +152,10 @@ public ScannerEngineBootstrapResult bootstrap() {
151152
ScannerEngineFacade scannerFacade;
152153
if (isSonarCloud || VersionUtils.isAtLeastIgnoringQualifier(serverVersion, SQ_VERSION_NEW_BOOTSTRAPPING)) {
153154
var launcher = scannerEngineLauncherFactory.createLauncher(scannerHttpClient, fileCache, immutableProperties);
154-
scannerFacade = new NewScannerEngineFacade(immutableProperties, launcher, isSonarCloud, serverVersion);
155+
156+
var adaptedProperties = adaptSslPropertiesToScannerProperties(immutableProperties, httpConfig);
157+
158+
scannerFacade = new NewScannerEngineFacade(adaptedProperties, launcher, isSonarCloud, serverVersion);
155159
} else {
156160
var launcher = launcherFactory.createLauncher(scannerHttpClient, fileCache);
157161
var adaptedProperties = adaptDeprecatedPropertiesForInProcessBootstrapping(immutableProperties, httpConfig);
@@ -215,11 +219,12 @@ Map<String, String> adaptDeprecatedPropertiesForInProcessBootstrapping(Map<Strin
215219
}
216220
var proxy = httpConfig.getProxy();
217221
if (proxy != null) {
218-
setSystemPropertyIfNotAlreadySet("http.proxyHost", ((InetSocketAddress) proxy.address()).getHostString());
219-
setSystemPropertyIfNotAlreadySet("https.proxyHost", ((InetSocketAddress) proxy.address()).getHostString());
220-
setSystemPropertyIfNotAlreadySet("http.proxyPort", "" + ((InetSocketAddress) proxy.address()).getPort());
221-
setSystemPropertyIfNotAlreadySet("https.proxyPort", "" + ((InetSocketAddress) proxy.address()).getPort());
222+
setSystemPropertyIfNotAlreadySet(HTTP_PROXY_HOST, ((InetSocketAddress) proxy.address()).getHostString());
223+
setSystemPropertyIfNotAlreadySet(HTTPS_PROXY_HOST, ((InetSocketAddress) proxy.address()).getHostString());
224+
setSystemPropertyIfNotAlreadySet(HTTP_PROXY_PORT, String.valueOf(((InetSocketAddress) proxy.address()).getPort()));
225+
setSystemPropertyIfNotAlreadySet(HTTPS_PROXY_PORT, String.valueOf(((InetSocketAddress) proxy.address()).getPort()));
222226
}
227+
// Those are not standard JVM properties, but they are supported by the Scanner Engine.
223228
setSystemPropertyIfNotAlreadySet("http.proxyUser", httpConfig.getProxyUser());
224229
setSystemPropertyIfNotAlreadySet("http.proxyPassword", httpConfig.getProxyPassword());
225230

@@ -237,7 +242,7 @@ Map<String, String> adaptDeprecatedPropertiesForInProcessBootstrapping(Map<Strin
237242
return Map.copyOf(adaptedProperties);
238243
}
239244

240-
private void setSystemPropertyIfNotAlreadySet(String key, String value) {
245+
private void setSystemPropertyIfNotAlreadySet(String key, @Nullable String value) {
241246
if (system.getProperty(key) == null && StringUtils.isNotBlank(value)) {
242247
System.setProperty(key, value);
243248
}
@@ -290,30 +295,20 @@ private void initBootstrapDefaultValues() {
290295
* by inserting the trusted certificate in the Scanner JVM truststore, or passing JVM SSL properties
291296
* we need to adapt the properties, at least temporarily, until we have helped most users to migrate.
292297
*/
293-
static void adaptJvmSslPropertiesToScannerProperties(Map<String, String> bootstrapProperties, System2 system) {
294-
if (!bootstrapProperties.containsKey(SONAR_SCANNER_TRUSTSTORE_PATH)) {
295-
var jvmTrustStoreProp = system.getProperty(JAVAX_NET_SSL_TRUST_STORE);
296-
if (StringUtils.isBlank(jvmTrustStoreProp)) {
297-
var defaultJvmTrustStoreLocation = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts");
298-
if (Files.isRegularFile(defaultJvmTrustStoreLocation)) {
299-
LOG.debug("Mapping default scanner JVM truststore location '{}' to new properties", defaultJvmTrustStoreLocation);
300-
bootstrapProperties.put(SONAR_SCANNER_TRUSTSTORE_PATH, defaultJvmTrustStoreLocation.toString());
301-
bootstrapProperties.putIfAbsent(SONAR_SCANNER_TRUSTSTORE_PASSWORD, System.getProperty(JAVAX_NET_SSL_TRUST_STORE_PASSWORD, "changeit"));
302-
}
303-
} else {
304-
bootstrapProperties.putIfAbsent(SONAR_SCANNER_TRUSTSTORE_PATH, jvmTrustStoreProp);
305-
ofNullable(system.getProperty(JAVAX_NET_SSL_TRUST_STORE_PASSWORD))
306-
.ifPresent(password -> bootstrapProperties.putIfAbsent(SONAR_SCANNER_TRUSTSTORE_PASSWORD, password));
307-
}
298+
static Map<String, String> adaptSslPropertiesToScannerProperties(Map<String, String> bootstrapProperties, HttpConfig httpConfig) {
299+
var result = new HashMap<>(bootstrapProperties);
300+
var keyStore = httpConfig.getSslConfig().getKeyStore();
301+
if (keyStore != null && keyStore.isFromJvm()) {
302+
result.put(SONAR_SCANNER_KEYSTORE_PATH, keyStore.getPath().toString());
303+
keyStore.getKeyStorePassword().ifPresent(password -> result.put(SONAR_SCANNER_KEYSTORE_PASSWORD, password));
308304
}
309-
if (!bootstrapProperties.containsKey(SONAR_SCANNER_KEYSTORE_PATH)) {
310-
var keystoreProp = system.getProperty(JAVAX_NET_SSL_KEY_STORE);
311-
if (!StringUtils.isBlank(keystoreProp)) {
312-
bootstrapProperties.put(SONAR_SCANNER_KEYSTORE_PATH, keystoreProp);
313-
ofNullable(system.getProperty(JAVAX_NET_SSL_KEY_STORE_PASSWORD))
314-
.ifPresent(password -> bootstrapProperties.putIfAbsent(SONAR_SCANNER_KEYSTORE_PASSWORD, password));
315-
}
305+
306+
var trustStore = httpConfig.getSslConfig().getTrustStore();
307+
if (trustStore != null && trustStore.isFromJvm()) {
308+
result.put(SONAR_SCANNER_TRUSTSTORE_PATH, trustStore.getPath().toString());
309+
trustStore.getKeyStorePassword().ifPresent(password -> result.put(SONAR_SCANNER_TRUSTSTORE_PASSWORD, password));
316310
}
311+
return Map.copyOf(result);
317312
}
318313

319314
private String getSonarCloudUrl() {

lib/src/main/java/org/sonarsource/scanner/lib/ScannerProperties.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ private ScannerProperties() {
8787
public static final String SONAR_SCANNER_TRUSTSTORE_PATH = "sonar.scanner.truststorePath";
8888
public static final String SONAR_SCANNER_TRUSTSTORE_PASSWORD = "sonar.scanner.truststorePassword";
8989
public static final String SONAR_SCANNER_SKIP_SYSTEM_TRUSTSTORE = "sonar.scanner.skipSystemTruststore";
90+
9091
/**
9192
* Skip analysis.
9293
*/
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* SonarScanner Java Library
3+
* Copyright (C) 2011-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonarsource.scanner.lib.internal;
21+
22+
/**
23+
* See <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/doc-files/net-properties.html">...</a>
24+
*/
25+
public class JvmProperties {
26+
public static final String JAVAX_NET_SSL_TRUST_STORE = "javax.net.ssl.trustStore";
27+
public static final String JAVAX_NET_SSL_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword";
28+
public static final String JAVAX_NET_SSL_KEY_STORE = "javax.net.ssl.keyStore";
29+
public static final String JAVAX_NET_SSL_KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword";
30+
public static final String HTTP_PROXY_HOST = "http.proxyHost";
31+
public static final String HTTPS_PROXY_HOST = "https.proxyHost";
32+
public static final String HTTP_PROXY_PORT = "http.proxyPort";
33+
public static final String HTTPS_PROXY_PORT = "https.proxyPort";
34+
35+
private JvmProperties() {
36+
}
37+
}

0 commit comments

Comments
 (0)