diff --git a/pom.xml b/pom.xml index c801b758..2195fadd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.checkmarx.ast ast-cli-java-wrapper - 1.0.15 + 1.0.17 jar Checkmarx AST Client @@ -13,8 +13,8 @@ https://www.checkmarx.com - 8 - 8 + 8 + 8 @@ -66,12 +66,6 @@ 5.7.2 test - - junit - junit - 4.13.1 - test - diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java index c8ab5885..cdf58054 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java @@ -12,10 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -32,21 +29,21 @@ public class CxWrapper { @NonNull private final Logger logger; @NonNull - private final URI executable; + private final String executable; public CxWrapper(@NonNull CxConfig cxConfig) - throws CxConfig.InvalidCLIConfigException, URISyntaxException, IOException { + throws CxConfig.InvalidCLIConfigException, IOException { this(cxConfig, LoggerFactory.getLogger(CxWrapper.class)); } public CxWrapper(@NonNull CxConfig cxConfig, @NonNull Logger logger) throws CxConfig.InvalidCLIConfigException, - URISyntaxException, IOException { + IOException { cxConfig.validate(); this.cxConfig = cxConfig; this.logger = logger; this.executable = StringUtils.isBlank(this.cxConfig.getPathToExecutable()) - ? Execution.detectBinary() - : new File(this.cxConfig.getPathToExecutable()).toURI(); + ? Execution.getTempBinary() + : this.cxConfig.getPathToExecutable(); this.logger.info("using executable: " + executable); } @@ -189,7 +186,7 @@ public String results(@NonNull UUID scanId, ReportFormat reportFormat) private List withConfigArguments(List commands) { List arguments = new ArrayList<>(); - arguments.add(this.executable.getPath()); + arguments.add(this.executable); arguments.addAll(commands); arguments.addAll(this.cxConfig.toArguments()); diff --git a/src/main/java/com/checkmarx/ast/wrapper/Execution.java b/src/main/java/com/checkmarx/ast/wrapper/Execution.java index 094457de..1520cb69 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/Execution.java +++ b/src/main/java/com/checkmarx/ast/wrapper/Execution.java @@ -3,16 +3,17 @@ import org.slf4j.Logger; import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.function.Function; public final class Execution { @@ -29,8 +30,9 @@ private Execution() { private static final String FILE_NAME_MAC = "cx-mac"; private static final String FILE_NAME_WINDOWS = "cx.exe"; private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String TEMP_DIR = System.getProperty("java.io.tmpdir"); - private static URL executable = null; + private static String executable = null; static T executeCommand(List arguments, Logger logger, @@ -83,6 +85,29 @@ static String executeCommand(List arguments, StandardCharsets.UTF_8); } + static String getTempBinary() throws IOException { + if (executable == null) { + String fileName = detectBinaryName(); + if (fileName == null) { + throw new IOException("Unsupported architecture"); + } + URL resource = Execution.class.getClassLoader().getResource(fileName); + if (resource == null) { + throw new NoSuchFileException("Could not find CLI executable"); + } + File tempExecutable = new File(TEMP_DIR, fileName); + if (!tempExecutable.exists() || !compareChecksum(resource.openStream(), + new FileInputStream(tempExecutable))) { + copyURLToFile(resource, tempExecutable); + } + if (!tempExecutable.canExecute() && !tempExecutable.setExecutable(true)) { + throw new IOException("Could not set CLI as executable"); + } + executable = tempExecutable.getAbsolutePath(); + } + return executable; + } + private static BufferedReader getReader(Process process) { InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); @@ -95,38 +120,56 @@ private static Process buildProcess(List commands) throws IOException { return lmBuilder.start(); } - /** - * Detect binary name by the current architecture. - * - * @return binary name - * @throws IOException when architecture is unsupported - * @throws URISyntaxException when the file has an invalid URI - */ - public static URI detectBinary() throws IOException, URISyntaxException { - if (executable == null) { - final String arch = OS_NAME; - String fileName = null; - if (arch.contains(OS_LINUX)) { - fileName = FILE_NAME_LINUX; - } else if (arch.contains(OS_WINDOWS)) { - fileName = FILE_NAME_WINDOWS; - } else { - for (String macStr : OS_MAC) { - if (arch.contains(macStr)) { - fileName = FILE_NAME_MAC; - break; - } + private static String detectBinaryName() { + String arch = OS_NAME; + String fileName = null; + if (arch.contains(OS_LINUX)) { + fileName = FILE_NAME_LINUX; + } else if (arch.contains(OS_WINDOWS)) { + fileName = FILE_NAME_WINDOWS; + } else { + for (String macStr : OS_MAC) { + if (arch.contains(macStr)) { + fileName = FILE_NAME_MAC; + break; } } - if (fileName == null) { - throw new IOException("Unsupported architecture"); + } + return fileName; + } + + private static void copyURLToFile(URL source, File destination) throws IOException { + final byte[] buf = new byte[8192]; + try (InputStream reader = source.openStream(); + OutputStream writer = new FileOutputStream(destination)) { + int i; + while ((i = reader.read(buf)) != -1) { + writer.write(buf, 0, i); } - executable = Execution.class.getClassLoader().getResource(fileName); + } catch (IOException e) { + throw new IOException("Could not copy CLI to the temporary directory", e); } - URL resource = executable; - if (resource == null) { - throw new NoSuchFileException("Could not find CLI executable"); + } + + private static boolean compareChecksum(InputStream a, InputStream b) { + String aMD5 = md5(a); + String bMD5 = md5(b); + return aMD5 != null && bMD5 != null && Objects.equals(aMD5, bMD5); + } + + private static String md5(InputStream a) { + String md5 = null; + final byte[] buf = new byte[8192]; + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + int i; + while ((i = a.read(buf)) != -1) { + md.update(buf, 0, i); + } + md5 = new String(md.digest()); + } catch (NoSuchAlgorithmException | IOException e) { + // ignore } - return resource.toURI(); + return md5; } }