Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCAN4NET-211 Add ITs with the new truststore properties #2309

Draft
wants to merge 5 commits into
base: sma/props-map-2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 130 additions & 11 deletions its/src/test/java/com/sonar/it/scanner/msbuild/sonarqube/SslTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.sonar.orchestrator.build.BuildResult;
import com.sonar.orchestrator.locator.FileLocation;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -41,6 +40,7 @@

import static com.sonar.it.scanner.msbuild.sonarqube.Tests.ORCHESTRATOR;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -126,14 +126,9 @@ void trustedSelfSignedCertificate() throws Exception {

TestUtils.buildMSBuild(ORCHESTRATOR, projectDir);

BuildResult result = TestUtils.executeEndStepAndDumpResults(
ORCHESTRATOR,
projectDir,
projectKey, token,
List.of(
new EnvironmentVariable("SONAR_SCANNER_OPTS", "-Djavax.net.ssl.trustStore=" + keystorePath.replace('\\', '/') + " -Djavax.net.ssl.trustStorePassword=" + keystorePassword)
)
);
var env = List.of(new EnvironmentVariable("SONAR_SCANNER_OPTS", "-Djavax.net.ssl.trustStore=" + keystorePath.replace('\\', '/') + " -Djavax.net.ssl.trustStorePassword=" + keystorePassword));
BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token, env);

assertTrue(result.isSuccess());
List<Issues.Issue> issues = TestUtils.allIssues(ORCHESTRATOR);
assertThat(issues).hasSize(1);
Expand Down Expand Up @@ -168,9 +163,133 @@ void untrustedSelfSignedCertificate() throws Exception {
}
}

@Test
void selfSignedCertificateInGivenTrustStore() throws Exception {
var projectKey = PROJECT_KEY + "-truststore";
ORCHESTRATOR.getServer().restoreProfile(FileLocation.of("projects/ProjectUnderTest/TestQualityProfile.xml"));
ORCHESTRATOR.getServer().provisionProject(projectKey, "sample");
ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cs", "ProfileForTest");
var trustStorePassword = "changeit";
var trustStorePath = createKeyStore(trustStorePassword);
var server = new HttpsReverseProxy(ORCHESTRATOR.getServer().getUrl(), trustStorePath, trustStorePassword);
server.start();

String token = TestUtils.getNewToken(ORCHESTRATOR);

Path projectDir = TestUtils.projectDir(basePath, "ProjectUnderTest");
ORCHESTRATOR.executeBuild(TestUtils.newScanner(ORCHESTRATOR, projectDir, token)
.addArgument("begin")
.setProjectKey(projectKey)
.setProperty("sonar.scanner.truststorePath", trustStorePath)
.setProperty("sonar.scanner.truststorePassword", trustStorePassword)
.setProperty("sonar.host.url", server.getUrl())
.setProjectName("sample")
.setProperty("sonar.projectBaseDir", projectDir.toAbsolutePath().toString())
.setProjectVersion("1.0"));

TestUtils.buildMSBuild(ORCHESTRATOR, projectDir);

BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);

assertTrue(result.isSuccess());
assertThat(result.getLogs())
.contains("SONAR_SCANNER_OPTS")
.contains("-Djavax.net.ssl.trustStore=\"" + trustStorePath.replace('\\', '/') + "\"")
.contains("-Djavax.net.ssl.trustStorePassword=\"" + trustStorePassword + "\"");
List<Issues.Issue> issues = TestUtils.allIssues(ORCHESTRATOR);
assertThat(issues).hasSize(1);
server.stop();
}

@Test
void selfSignedCertificateInGivenTrustStore_PathAndPasswordWithSpace() throws Exception {
// We don't support spaces in the truststore path and password on Unix
// Running this test on Linux would always fail
assumeThat(System.getProperty("os.name")).contains("Windows");

var projectKey = PROJECT_KEY + "-spaced-truststore";
ORCHESTRATOR.getServer().restoreProfile(FileLocation.of("projects/ProjectUnderTest/TestQualityProfile.xml"));
ORCHESTRATOR.getServer().provisionProject(projectKey, "sample");
ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, "cs", "ProfileForTest");
var trustStorePassword = "change it";
var trustStorePath = createKeyStore(trustStorePassword, Path.of("sub", "folder with spaces"));
var server = new HttpsReverseProxy(ORCHESTRATOR.getServer().getUrl(), trustStorePath, trustStorePassword);
server.start();

String token = TestUtils.getNewToken(ORCHESTRATOR);

Path projectDir = TestUtils.projectDir(basePath, "ProjectUnderTest");
ORCHESTRATOR.executeBuild(TestUtils.newScanner(ORCHESTRATOR, projectDir, token)
.addArgument("begin")
.setProjectKey(projectKey)
.setProperty("sonar.scanner.truststorePath", trustStorePath)
.setProperty("sonar.scanner.truststorePassword", trustStorePassword)
.setProperty("sonar.host.url", server.getUrl())
.setProjectName("sample")
.setProperty("sonar.projectBaseDir", projectDir.toAbsolutePath().toString())
.setProjectVersion("1.0"));

TestUtils.buildMSBuild(ORCHESTRATOR, projectDir);

BuildResult result = TestUtils.executeEndStepAndDumpResults(ORCHESTRATOR, projectDir, projectKey, token);

assertTrue(result.isSuccess());
assertThat(result.getLogs())
.contains("SONAR_SCANNER_OPTS")
.contains("-Djavax.net.ssl.trustStore=\"" + trustStorePath.replace('\\', '/') + "\"")
.contains("-Djavax.net.ssl.trustStorePassword=\"" + trustStorePassword + "\"");
List<Issues.Issue> issues = TestUtils.allIssues(ORCHESTRATOR);
assertThat(issues).hasSize(1);
server.stop();
}

@Test
void unmatchedDomainNameInCertificate() throws Exception {
var projectKey = PROJECT_KEY + "-unmatched-domain";
var trustStorePassword = "changeit";
var trustStorePath = createKeyStore(trustStorePassword, "not-localhost");
var server = new HttpsReverseProxy(ORCHESTRATOR.getServer().getUrl(), trustStorePath, trustStorePassword);
server.start();

String token = TestUtils.getNewToken(ORCHESTRATOR);

Path projectDir = TestUtils.projectDir(basePath, "ProjectUnderTest");

try {
ORCHESTRATOR.executeBuild(TestUtils.newScanner(ORCHESTRATOR, projectDir, token)
.addArgument("begin")
.setProjectKey(projectKey)
.setProperty("sonar.scanner.truststorePath", trustStorePath)
.setProperty("sonar.scanner.truststorePassword", trustStorePassword)
.setProperty("sonar.host.url", server.getUrl())
.setProjectName("sample")
.setProperty("sonar.projectBaseDir", projectDir.toAbsolutePath().toString())
.setProjectVersion("1.0"));
} catch (BuildFailureException e) {
assertFalse(e.getResult().isSuccess());
assertThat(e.getResult().getLogs())
.contains("System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.")
.contains("System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.");
} finally {
server.stop();
}
}

private String createKeyStore(String password) {
var keystoreLocation = basePath.resolve("keystore.pfx").toAbsolutePath();
return createKeyStore(password, Path.of(""), "localhost");
}

private String createKeyStore(String password, Path subFolder) {
return createKeyStore(password, subFolder, "localhost");
}

private String createKeyStore(String password, String host) {
return createKeyStore(password, Path.of(""), host);
}

private String createKeyStore(String password, Path subFolder, String host) {
var keystoreLocation = basePath.resolve(subFolder.resolve("keystore.pfx")).toAbsolutePath();
LOG.info("Creating keystore at {}", keystoreLocation);
return SslUtils.generateKeyStore(keystoreLocation, "localhost", password);
return SslUtils.generateKeyStore(keystoreLocation, host, password);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public static String generateKeyStore(Path outputPath, String host, String passw
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);

outputPath.getParent().toFile().mkdirs();

Certificate[] chain = {cert};
keyStore.setKeyEntry("key", keyPair.getPrivate(), password.toCharArray(), chain);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ IDownloader CreateDownloader(string baseUrl) =>
new WebClientDownloaderBuilder(baseUrl, args.HttpTimeout, logger)
.AddAuthorization(userName, password)
.AddCertificate(clientCertPath, clientCertPassword)
.AddServerCertificate(args.TruststorePath, args.TruststorePassword)
.Build();
}

Expand Down