Skip to content

Commit cf4e819

Browse files
if no path is provided to the wrapper, extract the executable to temp and use it (#34)
* add prototype solution for testing in eclipse * 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 * bump pom to 1.0.18 to match the next tag when released * revert version to test the release process Co-authored-by: Pedro Lopes <[email protected]>
1 parent 35a2309 commit cf4e819

File tree

3 files changed

+83
-49
lines changed

3 files changed

+83
-49
lines changed

pom.xml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55

66
<groupId>com.checkmarx.ast</groupId>
77
<artifactId>ast-cli-java-wrapper</artifactId>
8-
<version>1.0.15</version>
8+
<version>1.0.17</version>
99
<packaging>jar</packaging>
1010

1111
<name>Checkmarx AST Client</name>
1212
<description>Checkmarx AST ClI SDK</description>
1313
<url>https://www.checkmarx.com</url>
1414

1515
<properties>
16-
<maven.compiler.source>8</maven.compiler.source>
17-
<maven.compiler.target>8</maven.compiler.target>
16+
<maven.compiler.source>8</maven.compiler.source>
17+
<maven.compiler.target>8</maven.compiler.target>
1818
</properties>
1919

2020
<dependencies>
@@ -66,12 +66,6 @@
6666
<version>5.7.2</version>
6767
<scope>test</scope>
6868
</dependency>
69-
<dependency>
70-
<groupId>junit</groupId>
71-
<artifactId>junit</artifactId>
72-
<version>4.13.1</version>
73-
<scope>test</scope>
74-
</dependency>
7569
</dependencies>
7670

7771
<build>

src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@
1212
import org.slf4j.Logger;
1313
import org.slf4j.LoggerFactory;
1414

15-
import java.io.File;
1615
import java.io.IOException;
17-
import java.net.URI;
18-
import java.net.URISyntaxException;
1916
import java.nio.file.Files;
2017
import java.util.ArrayList;
2118
import java.util.List;
@@ -32,21 +29,21 @@ public class CxWrapper {
3229
@NonNull
3330
private final Logger logger;
3431
@NonNull
35-
private final URI executable;
32+
private final String executable;
3633

3734
public CxWrapper(@NonNull CxConfig cxConfig)
38-
throws CxConfig.InvalidCLIConfigException, URISyntaxException, IOException {
35+
throws CxConfig.InvalidCLIConfigException, IOException {
3936
this(cxConfig, LoggerFactory.getLogger(CxWrapper.class));
4037
}
4138

4239
public CxWrapper(@NonNull CxConfig cxConfig, @NonNull Logger logger) throws CxConfig.InvalidCLIConfigException,
43-
URISyntaxException, IOException {
40+
IOException {
4441
cxConfig.validate();
4542
this.cxConfig = cxConfig;
4643
this.logger = logger;
4744
this.executable = StringUtils.isBlank(this.cxConfig.getPathToExecutable())
48-
? Execution.detectBinary()
49-
: new File(this.cxConfig.getPathToExecutable()).toURI();
45+
? Execution.getTempBinary()
46+
: this.cxConfig.getPathToExecutable();
5047
this.logger.info("using executable: " + executable);
5148
}
5249

@@ -189,7 +186,7 @@ public String results(@NonNull UUID scanId, ReportFormat reportFormat)
189186
private List<String> withConfigArguments(List<String> commands) {
190187
List<String> arguments = new ArrayList<>();
191188

192-
arguments.add(this.executable.getPath());
189+
arguments.add(this.executable);
193190
arguments.addAll(commands);
194191
arguments.addAll(this.cxConfig.toArguments());
195192

src/main/java/com/checkmarx/ast/wrapper/Execution.java

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
import org.slf4j.Logger;
44

55
import java.io.*;
6-
import java.net.URI;
7-
import java.net.URISyntaxException;
86
import java.net.URL;
97
import java.nio.charset.StandardCharsets;
108
import java.nio.file.Files;
119
import java.nio.file.NoSuchFileException;
1210
import java.nio.file.Paths;
11+
import java.security.MessageDigest;
12+
import java.security.NoSuchAlgorithmException;
1313
import java.util.Arrays;
1414
import java.util.List;
1515
import java.util.Locale;
16+
import java.util.Objects;
1617
import java.util.function.Function;
1718

1819
public final class Execution {
@@ -29,8 +30,9 @@ private Execution() {
2930
private static final String FILE_NAME_MAC = "cx-mac";
3031
private static final String FILE_NAME_WINDOWS = "cx.exe";
3132
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
33+
private static final String TEMP_DIR = System.getProperty("java.io.tmpdir");
3234

33-
private static URL executable = null;
35+
private static String executable = null;
3436

3537
static <T> T executeCommand(List<String> arguments,
3638
Logger logger,
@@ -83,6 +85,29 @@ static String executeCommand(List<String> arguments,
8385
StandardCharsets.UTF_8);
8486
}
8587

88+
static String getTempBinary() throws IOException {
89+
if (executable == null) {
90+
String fileName = detectBinaryName();
91+
if (fileName == null) {
92+
throw new IOException("Unsupported architecture");
93+
}
94+
URL resource = Execution.class.getClassLoader().getResource(fileName);
95+
if (resource == null) {
96+
throw new NoSuchFileException("Could not find CLI executable");
97+
}
98+
File tempExecutable = new File(TEMP_DIR, fileName);
99+
if (!tempExecutable.exists() || !compareChecksum(resource.openStream(),
100+
new FileInputStream(tempExecutable))) {
101+
copyURLToFile(resource, tempExecutable);
102+
}
103+
if (!tempExecutable.canExecute() && !tempExecutable.setExecutable(true)) {
104+
throw new IOException("Could not set CLI as executable");
105+
}
106+
executable = tempExecutable.getAbsolutePath();
107+
}
108+
return executable;
109+
}
110+
86111
private static BufferedReader getReader(Process process) {
87112
InputStream is = process.getInputStream();
88113
InputStreamReader isr = new InputStreamReader(is);
@@ -95,38 +120,56 @@ private static Process buildProcess(List<String> commands) throws IOException {
95120
return lmBuilder.start();
96121
}
97122

98-
/**
99-
* Detect binary name by the current architecture.
100-
*
101-
* @return binary name
102-
* @throws IOException when architecture is unsupported
103-
* @throws URISyntaxException when the file has an invalid URI
104-
*/
105-
public static URI detectBinary() throws IOException, URISyntaxException {
106-
if (executable == null) {
107-
final String arch = OS_NAME;
108-
String fileName = null;
109-
if (arch.contains(OS_LINUX)) {
110-
fileName = FILE_NAME_LINUX;
111-
} else if (arch.contains(OS_WINDOWS)) {
112-
fileName = FILE_NAME_WINDOWS;
113-
} else {
114-
for (String macStr : OS_MAC) {
115-
if (arch.contains(macStr)) {
116-
fileName = FILE_NAME_MAC;
117-
break;
118-
}
123+
private static String detectBinaryName() {
124+
String arch = OS_NAME;
125+
String fileName = null;
126+
if (arch.contains(OS_LINUX)) {
127+
fileName = FILE_NAME_LINUX;
128+
} else if (arch.contains(OS_WINDOWS)) {
129+
fileName = FILE_NAME_WINDOWS;
130+
} else {
131+
for (String macStr : OS_MAC) {
132+
if (arch.contains(macStr)) {
133+
fileName = FILE_NAME_MAC;
134+
break;
119135
}
120136
}
121-
if (fileName == null) {
122-
throw new IOException("Unsupported architecture");
137+
}
138+
return fileName;
139+
}
140+
141+
private static void copyURLToFile(URL source, File destination) throws IOException {
142+
final byte[] buf = new byte[8192];
143+
try (InputStream reader = source.openStream();
144+
OutputStream writer = new FileOutputStream(destination)) {
145+
int i;
146+
while ((i = reader.read(buf)) != -1) {
147+
writer.write(buf, 0, i);
123148
}
124-
executable = Execution.class.getClassLoader().getResource(fileName);
149+
} catch (IOException e) {
150+
throw new IOException("Could not copy CLI to the temporary directory", e);
125151
}
126-
URL resource = executable;
127-
if (resource == null) {
128-
throw new NoSuchFileException("Could not find CLI executable");
152+
}
153+
154+
private static boolean compareChecksum(InputStream a, InputStream b) {
155+
String aMD5 = md5(a);
156+
String bMD5 = md5(b);
157+
return aMD5 != null && bMD5 != null && Objects.equals(aMD5, bMD5);
158+
}
159+
160+
private static String md5(InputStream a) {
161+
String md5 = null;
162+
final byte[] buf = new byte[8192];
163+
try {
164+
MessageDigest md = MessageDigest.getInstance("MD5");
165+
int i;
166+
while ((i = a.read(buf)) != -1) {
167+
md.update(buf, 0, i);
168+
}
169+
md5 = new String(md.digest());
170+
} catch (NoSuchAlgorithmException | IOException e) {
171+
// ignore
129172
}
130-
return resource.toURI();
173+
return md5;
131174
}
132175
}

0 commit comments

Comments
 (0)