1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.analyzer;
19
20 import java.io.File;
21 import java.io.FileFilter;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import org.apache.commons.io.IOUtils;
26 import org.apache.commons.io.output.NullOutputStream;
27 import org.owasp.dependencycheck.Engine;
28 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
29 import org.owasp.dependencycheck.dependency.Confidence;
30 import org.owasp.dependencycheck.dependency.Dependency;
31 import org.owasp.dependencycheck.dependency.Evidence;
32 import org.owasp.dependencycheck.utils.FileFilterBuilder;
33 import org.owasp.dependencycheck.utils.Settings;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.w3c.dom.Document;
37 import org.xml.sax.SAXException;
38
39 import javax.xml.parsers.DocumentBuilder;
40 import javax.xml.parsers.DocumentBuilderFactory;
41 import javax.xml.xpath.XPath;
42 import javax.xml.xpath.XPathExpressionException;
43 import javax.xml.xpath.XPathFactory;
44 import java.util.ArrayList;
45 import java.util.List;
46 import javax.xml.parsers.ParserConfigurationException;
47 import org.owasp.dependencycheck.exception.InitializationException;
48 import org.apache.commons.lang3.SystemUtils;
49
50
51
52
53
54
55
56
57 public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
58
59
60
61
62 private static final String ANALYZER_NAME = "Assembly Analyzer";
63
64
65
66 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
67
68
69
70 private static final String[] SUPPORTED_EXTENSIONS = {"dll", "exe"};
71
72
73
74 private File grokAssemblyExe = null;
75
76
77
78 private DocumentBuilder builder;
79
80
81
82 private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyAnalyzer.class);
83
84
85
86
87
88
89 protected List<String> buildArgumentList() {
90
91 final List<String> args = new ArrayList<String>();
92 if (!SystemUtils.IS_OS_WINDOWS) {
93 if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
94 args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
95 } else if (isInPath("mono")) {
96 args.add("mono");
97 } else {
98 return null;
99 }
100 }
101 args.add(grokAssemblyExe.getPath());
102 return args;
103 }
104
105
106
107
108
109
110
111
112 @Override
113 public void analyzeFileType(Dependency dependency, Engine engine)
114 throws AnalysisException {
115 if (grokAssemblyExe == null) {
116 LOGGER.warn("GrokAssembly didn't get deployed");
117 return;
118 }
119
120 final List<String> args = buildArgumentList();
121 if (args == null) {
122 LOGGER.warn("Assembly Analyzer was unable to execute");
123 return;
124 }
125 args.add(dependency.getActualFilePath());
126 final ProcessBuilder pb = new ProcessBuilder(args);
127 Document doc = null;
128 try {
129 final Process proc = pb.start();
130
131 doc = builder.parse(proc.getInputStream());
132
133
134 final String errorStream = IOUtils.toString(proc.getErrorStream(), "UTF-8");
135 if (null != errorStream && !errorStream.isEmpty()) {
136 LOGGER.warn("Error from GrokAssembly: {}", errorStream);
137 }
138
139 int rc = 0;
140 try {
141 rc = proc.waitFor();
142 } catch (InterruptedException ie) {
143 return;
144 }
145 if (rc == 3) {
146 LOGGER.debug("{} is not a .NET assembly or executable and as such cannot be analyzed by dependency-check",
147 dependency.getActualFilePath());
148 return;
149 } else if (rc != 0) {
150 LOGGER.warn("Return code {} from GrokAssembly", rc);
151 }
152
153 final XPath xpath = XPathFactory.newInstance().newXPath();
154
155
156 final String error = xpath.evaluate("/assembly/error", doc);
157 if (error != null && !error.isEmpty()) {
158 throw new AnalysisException(error);
159 }
160
161 final String version = xpath.evaluate("/assembly/version", doc);
162 if (version != null) {
163 dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version",
164 version, Confidence.HIGHEST));
165 }
166
167 final String vendor = xpath.evaluate("/assembly/company", doc);
168 if (vendor != null) {
169 dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor",
170 vendor, Confidence.HIGH));
171 }
172
173 final String product = xpath.evaluate("/assembly/product", doc);
174 if (product != null) {
175 dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product",
176 product, Confidence.HIGH));
177 }
178
179 } catch (IOException ioe) {
180 throw new AnalysisException(ioe);
181 } catch (SAXException saxe) {
182 throw new AnalysisException("Couldn't parse GrokAssembly result", saxe);
183 } catch (XPathExpressionException xpe) {
184
185 throw new AnalysisException(xpe);
186 }
187 }
188
189
190
191
192
193
194
195 @Override
196 public void initializeFileTypeAnalyzer() throws InitializationException {
197 final File tempFile;
198 try {
199 tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
200 } catch (IOException ex) {
201 setEnabled(false);
202 throw new InitializationException("Unable to create temporary file for the assembly analyzerr", ex);
203 }
204 FileOutputStream fos = null;
205 InputStream is = null;
206 try {
207 fos = new FileOutputStream(tempFile);
208 is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
209 IOUtils.copy(is, fos);
210
211 grokAssemblyExe = tempFile;
212 LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
213 } catch (IOException ioe) {
214 this.setEnabled(false);
215 LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
216 throw new InitializationException("Could not extract GrokAssembly.exe", ioe);
217 } finally {
218 if (fos != null) {
219 try {
220 fos.close();
221 } catch (Throwable e) {
222 LOGGER.debug("Error closing output stream");
223 }
224 }
225 if (is != null) {
226 try {
227 is.close();
228 } catch (Throwable e) {
229 LOGGER.debug("Error closing input stream");
230 }
231 }
232 }
233
234
235 final List<String> args = buildArgumentList();
236
237
238
239
240
241
242
243 if (args == null) {
244 setEnabled(false);
245 LOGGER.error("----------------------------------------------------");
246 LOGGER.error(".NET Assembly Analyzer could not be initialized and at least one "
247 + "'exe' or 'dll' was scanned. The 'mono' executale could not be found on "
248 + "the path; either disable the Assembly Analyzer or configure the path mono.");
249 LOGGER.error("----------------------------------------------------");
250 return;
251 }
252 try {
253 final ProcessBuilder pb = new ProcessBuilder(args);
254 final Process p = pb.start();
255
256 IOUtils.copy(p.getErrorStream(), NullOutputStream.NULL_OUTPUT_STREAM);
257
258 final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
259 final XPath xpath = XPathFactory.newInstance().newXPath();
260 final String error = xpath.evaluate("/assembly/error", doc);
261 if (p.waitFor() != 1 || error == null || error.isEmpty()) {
262 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
263 LOGGER.debug("GrokAssembly.exe is not working properly");
264 grokAssemblyExe = null;
265 setEnabled(false);
266 throw new InitializationException("Could not execute .NET AssemblyAnalyzer");
267 }
268 } catch (InitializationException e) {
269 setEnabled(false);
270 throw e;
271 } catch (Throwable e) {
272 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
273 + "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
274 LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
275 setEnabled(false);
276 throw new InitializationException("An error occurred with the .NET AssemblyAnalyzer", e);
277 }
278 try {
279 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
280 } catch (ParserConfigurationException ex) {
281 setEnabled(false);
282 throw new InitializationException("Error initializing the assembly analyzer", ex);
283 }
284 }
285
286
287
288
289
290
291 @Override
292 public void close() throws Exception {
293 super.close();
294 try {
295 if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
296 LOGGER.debug("Unable to delete temporary GrokAssembly.exe; attempting delete on exit");
297 grokAssemblyExe.deleteOnExit();
298 }
299 } catch (SecurityException se) {
300 LOGGER.debug("Can't delete temporary GrokAssembly.exe");
301 grokAssemblyExe.deleteOnExit();
302 }
303 }
304
305
306
307
308 private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(
309 SUPPORTED_EXTENSIONS).build();
310
311 @Override
312 protected FileFilter getFileFilter() {
313 return FILTER;
314 }
315
316
317
318
319
320
321 @Override
322 public String getName() {
323 return ANALYZER_NAME;
324 }
325
326
327
328
329
330
331 @Override
332 public AnalysisPhase getAnalysisPhase() {
333 return ANALYSIS_PHASE;
334 }
335
336
337
338
339
340
341
342 @Override
343 protected String getAnalyzerEnabledSettingKey() {
344 return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
345 }
346
347
348
349
350
351
352
353
354
355
356 private boolean isInPath(String file) {
357 final ProcessBuilder pb = new ProcessBuilder("which", file);
358 try {
359 final Process proc = pb.start();
360 final int retCode = proc.waitFor();
361 if (retCode == 0) {
362 return true;
363 }
364 } catch (IOException ex) {
365 LOGGER.debug("Path seach failed for " + file);
366 } catch (InterruptedException ex) {
367 LOGGER.debug("Path seach failed for " + file);
368 }
369 return false;
370 }
371 }