sonarqube-scan-action/src/main/run-sonar-scanner.js

152 lines
3.9 KiB
JavaScript

import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import { parseArgsStringToArgv } from "string-argv";
const KEYTOOL_MAIN_CLASS = "sun.security.tools.keytool.Main";
const TRUSTSTORE_PASSWORD = "changeit"; // default password of the Java truststore!
export async function runSonarScanner(
inputArgs,
projectBaseDir,
scannerDir,
runnerEnv = {}
) {
const { runnerDebug, runnerOs, runnerTemp, sonarRootCert, sonarcloudUrl } =
runnerEnv;
const scannerBin =
runnerOs === "Windows" ? "sonar-scanner.bat" : "sonar-scanner";
const scannerArgs = [];
/**
* Not sanitization is needed when populating scannerArgs.
* @actions/exec will take care of sanitizing the args it receives.
*/
if (sonarcloudUrl) {
scannerArgs.push(`-Dsonar.scanner.sonarcloudUrl=${sonarcloudUrl}`);
}
if (runnerDebug === "1") {
scannerArgs.push("--debug");
}
if (projectBaseDir) {
scannerArgs.push(`-Dsonar.projectBaseDir=${projectBaseDir}`);
}
// The SSL folder may exist on an uncleaned self-hosted runner
const sslFolder = path.join(os.homedir(), ".sonar", "ssl");
const truststoreFile = path.join(sslFolder, "truststore.p12");
const keytoolParams = {
scannerDir,
truststoreFile,
};
if (fs.existsSync(truststoreFile)) {
let aliasSonarIsPresent = true;
try {
await checkSonarAliasInTruststore(keytoolParams);
} catch (_) {
aliasSonarIsPresent = false;
core.info(
`Existing Scanner truststore ${truststoreFile} does not contain 'sonar' alias`
);
}
if (aliasSonarIsPresent) {
core.info(
`Removing 'sonar' alias from already existing Scanner truststore: ${truststoreFile}`
);
await deleteSonarAliasFromTruststore(keytoolParams);
}
}
if (sonarRootCert) {
core.info("Adding SSL certificate to the Scanner truststore");
const tempCertPath = path.join(runnerTemp, "tmpcert.pem");
try {
fs.unlinkSync(tempCertPath);
} catch (_) {
// File doesn't exist, ignore
}
fs.writeFileSync(tempCertPath, sonarRootCert);
fs.mkdirSync(sslFolder, { recursive: true });
await importCertificateToTruststore(keytoolParams, tempCertPath);
scannerArgs.push(
`-Dsonar.scanner.truststorePassword=${TRUSTSTORE_PASSWORD}`
);
}
if (inputArgs) {
/**
* No sanitization, but it is parsing a string into an array of arguments in a safe way (= no command execution),
* and with good enough support of quotes to support arguments containing spaces.
*/
const args = parseArgsStringToArgv(inputArgs);
scannerArgs.push(...args);
}
/**
* Arguments are sanitized by `exec`
*/
await exec.exec(scannerBin, scannerArgs);
}
/**
* Use keytool for now, as SonarQube 10.6 and below doesn't support openssl generated keystores
* keytool requires a password > 6 characters, so we won't use the default password 'sonar'
*/
function executeKeytoolCommand({
scannerDir,
truststoreFile,
extraArgs,
options = {},
}) {
const baseArgs = [
KEYTOOL_MAIN_CLASS,
"-storetype",
"PKCS12",
"-keystore",
truststoreFile,
"-storepass",
TRUSTSTORE_PASSWORD,
"-noprompt",
"-trustcacerts",
...extraArgs,
];
return exec.exec(`${scannerDir}/jre/bin/java`, baseArgs, options);
}
function importCertificateToTruststore(keytoolParams, certPath) {
return executeKeytoolCommand({
...keytoolParams,
extraArgs: ["-importcert", "-alias", "sonar", "-file", certPath],
});
}
function checkSonarAliasInTruststore(keytoolParams) {
return executeKeytoolCommand({
...keytoolParams,
extraArgs: ["-list", "-v", "-alias", "sonar"],
options: { silent: true },
});
}
function deleteSonarAliasFromTruststore(keytoolParams) {
return executeKeytoolCommand({
...keytoolParams,
extraArgs: ["-delete", "-alias", "sonar"],
});
}