From 1062b128fce2d099038a350f75f7865b39b7d285 Mon Sep 17 00:00:00 2001 From: Diogo Pimenta Date: Thu, 14 Oct 2021 17:47:55 +0100 Subject: [PATCH 1/4] add prototype solution for testing in eclipse --- pom.xml | 20 +++--- .../com/checkmarx/ast/wrapper/CxWrapper.java | 15 ++-- .../com/checkmarx/ast/wrapper/Execution.java | 72 +++++++++++-------- 3 files changed, 60 insertions(+), 47 deletions(-) diff --git a/pom.xml b/pom.xml index c801b758..054200f2 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,8 @@ https://www.checkmarx.com - 8 - 8 + 8 + 8 @@ -23,6 +23,16 @@ commons-lang3 3.11 + + commons-io + commons-io + 2.11.0 + + + commons-codec + commons-codec + 1.15 + com.googlecode.json-simple json-simple @@ -66,12 +76,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..63e775a6 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/Execution.java +++ b/src/main/java/com/checkmarx/ast/wrapper/Execution.java @@ -1,10 +1,10 @@ package com.checkmarx.ast.wrapper; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; 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; @@ -13,6 +13,7 @@ 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, @@ -95,38 +97,48 @@ 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 { + public static String getTempBinary() throws IOException { 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; - } - } - } + String fileName = detectBinaryName(); if (fileName == null) { throw new IOException("Unsupported architecture"); } - executable = Execution.class.getClassLoader().getResource(fileName); + 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))) { + FileUtils.copyURLToFile(resource, tempExecutable); + } + if (!tempExecutable.canExecute() && !tempExecutable.setExecutable(true)) { + throw new IOException("Could not set CLI as executable"); + } + executable = tempExecutable.getAbsolutePath(); } - URL resource = executable; - if (resource == null) { - throw new NoSuchFileException("Could not find CLI executable"); + return executable; + } + + 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; + } + } } - return resource.toURI(); + return fileName; + } + + private static boolean compareChecksum(InputStream a, InputStream b) throws IOException { + return Objects.equals(DigestUtils.md5Hex(a), DigestUtils.md5Hex(b)); } } From 5f0fbabe1a238193f76a92352d635f97c2c7aab6 Mon Sep 17 00:00:00 2001 From: Diogo Pimenta Date: Thu, 14 Oct 2021 18:30:14 +0100 Subject: [PATCH 2/4] working solution - if no path is provided, the wrapper will extract from its jar to a temporary file and cache the path - if the temporary file exists, it only copies if the md5 is not equal --- pom.xml | 10 --- .../com/checkmarx/ast/wrapper/Execution.java | 67 ++++++++++++++----- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index 054200f2..e0d51b3c 100644 --- a/pom.xml +++ b/pom.xml @@ -23,16 +23,6 @@ commons-lang3 3.11 - - commons-io - commons-io - 2.11.0 - - - commons-codec - commons-codec - 1.15 - com.googlecode.json-simple json-simple diff --git a/src/main/java/com/checkmarx/ast/wrapper/Execution.java b/src/main/java/com/checkmarx/ast/wrapper/Execution.java index 63e775a6..1520cb69 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/Execution.java +++ b/src/main/java/com/checkmarx/ast/wrapper/Execution.java @@ -1,7 +1,5 @@ package com.checkmarx.ast.wrapper; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import java.io.*; @@ -10,6 +8,8 @@ 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; @@ -85,19 +85,7 @@ static String executeCommand(List arguments, StandardCharsets.UTF_8); } - private static BufferedReader getReader(Process process) { - InputStream is = process.getInputStream(); - InputStreamReader isr = new InputStreamReader(is); - return new BufferedReader(isr); - } - - private static Process buildProcess(List commands) throws IOException { - ProcessBuilder lmBuilder = new ProcessBuilder(commands); - lmBuilder.redirectErrorStream(true); - return lmBuilder.start(); - } - - public static String getTempBinary() throws IOException { + static String getTempBinary() throws IOException { if (executable == null) { String fileName = detectBinaryName(); if (fileName == null) { @@ -110,7 +98,7 @@ public static String getTempBinary() throws IOException { File tempExecutable = new File(TEMP_DIR, fileName); if (!tempExecutable.exists() || !compareChecksum(resource.openStream(), new FileInputStream(tempExecutable))) { - FileUtils.copyURLToFile(resource, tempExecutable); + copyURLToFile(resource, tempExecutable); } if (!tempExecutable.canExecute() && !tempExecutable.setExecutable(true)) { throw new IOException("Could not set CLI as executable"); @@ -120,6 +108,18 @@ public static String getTempBinary() throws IOException { return executable; } + private static BufferedReader getReader(Process process) { + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + return new BufferedReader(isr); + } + + private static Process buildProcess(List commands) throws IOException { + ProcessBuilder lmBuilder = new ProcessBuilder(commands); + lmBuilder.redirectErrorStream(true); + return lmBuilder.start(); + } + private static String detectBinaryName() { String arch = OS_NAME; String fileName = null; @@ -138,7 +138,38 @@ private static String detectBinaryName() { return fileName; } - private static boolean compareChecksum(InputStream a, InputStream b) throws IOException { - return Objects.equals(DigestUtils.md5Hex(a), DigestUtils.md5Hex(b)); + 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); + } + } catch (IOException e) { + throw new IOException("Could not copy CLI to the temporary directory", e); + } + } + + 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 md5; } } From acb256112cd869c78451f25d2131bdd1db3d38e6 Mon Sep 17 00:00:00 2001 From: Diogo Pimenta Date: Thu, 14 Oct 2021 18:36:44 +0100 Subject: [PATCH 3/4] bump pom to 1.0.18 to match the next tag when released --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0d51b3c..d80da78c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.checkmarx.ast ast-cli-java-wrapper - 1.0.15 + 1.0.18 jar Checkmarx AST Client From eaae6fbb74603c5bec65542db8c4ce92693810c5 Mon Sep 17 00:00:00 2001 From: Pedro Lopes Date: Fri, 15 Oct 2021 09:03:05 +0100 Subject: [PATCH 4/4] revert version to test the release process --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d80da78c..2195fadd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.checkmarx.ast ast-cli-java-wrapper - 1.0.18 + 1.0.17 jar Checkmarx AST Client