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.BufferedReader;
21 import java.io.File;
22 import java.io.FileFilter;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
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
47
48
49
50
51
52
53 public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
54
55
56
57
58 private static final String ANALYZER_NAME = "Assembly Analyzer";
59
60
61
62 private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
63
64
65
66 private static final String[] SUPPORTED_EXTENSIONS = {"dll", "exe"};
67
68
69
70 private File grokAssemblyExe = null;
71
72
73
74 private DocumentBuilder builder;
75
76
77
78 private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyAnalyzer.class);
79
80
81
82
83
84
85 private List<String> buildArgumentList() {
86
87 final List<String> args = new ArrayList<String>();
88 if (!"\\".equals(System.getProperty("file.separator"))) {
89 if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
90 args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
91 } else {
92 args.add("mono");
93 }
94 }
95 args.add(grokAssemblyExe.getPath());
96
97 return args;
98 }
99
100
101
102
103
104
105
106
107 @Override
108 public void analyzeFileType(Dependency dependency, Engine engine)
109 throws AnalysisException {
110 if (grokAssemblyExe == null) {
111 LOGGER.warn("GrokAssembly didn't get deployed");
112 return;
113 }
114
115 final List<String> args = buildArgumentList();
116 args.add(dependency.getActualFilePath());
117 final ProcessBuilder pb = new ProcessBuilder(args);
118 BufferedReader rdr = null;
119 Document doc = null;
120 try {
121 final Process proc = pb.start();
122
123 rdr = new BufferedReader(new InputStreamReader(proc.getErrorStream(), "UTF-8"));
124 String line = null;
125
126 while (rdr.ready() && (line = rdr.readLine()) != null) {
127 LOGGER.warn("Error from GrokAssembly: {}", line);
128 }
129
130 int rc = 0;
131 doc = builder.parse(proc.getInputStream());
132
133 try {
134 rc = proc.waitFor();
135 } catch (InterruptedException ie) {
136 return;
137 }
138 if (rc == 3) {
139 LOGGER.debug("{} is not a .NET assembly or executable and as such cannot be analyzed by dependency-check",
140 dependency.getActualFilePath());
141 return;
142 } else if (rc != 0) {
143 LOGGER.warn("Return code {} from GrokAssembly", rc);
144 }
145
146 final XPath xpath = XPathFactory.newInstance().newXPath();
147
148
149 final String error = xpath.evaluate("/assembly/error", doc);
150 if (error != null && !error.isEmpty()) {
151 throw new AnalysisException(error);
152 }
153
154 final String version = xpath.evaluate("/assembly/version", doc);
155 if (version != null) {
156 dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version",
157 version, Confidence.HIGHEST));
158 }
159
160 final String vendor = xpath.evaluate("/assembly/company", doc);
161 if (vendor != null) {
162 dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor",
163 vendor, Confidence.HIGH));
164 }
165
166 final String product = xpath.evaluate("/assembly/product", doc);
167 if (product != null) {
168 dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product",
169 product, Confidence.HIGH));
170 }
171
172 } catch (IOException ioe) {
173 throw new AnalysisException(ioe);
174 } catch (SAXException saxe) {
175 throw new AnalysisException("Couldn't parse GrokAssembly result", saxe);
176 } catch (XPathExpressionException xpe) {
177
178 throw new AnalysisException(xpe);
179 } finally {
180 if (rdr != null) {
181 try {
182 rdr.close();
183 } catch (IOException ex) {
184 LOGGER.debug("ignore", ex);
185 }
186 }
187 }
188 }
189
190
191
192
193
194
195 @Override
196 public void initializeFileTypeAnalyzer() throws Exception {
197 final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
198 FileOutputStream fos = null;
199 InputStream is = null;
200 try {
201 fos = new FileOutputStream(tempFile);
202 is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
203 final byte[] buff = new byte[4096];
204 int bread = -1;
205 while ((bread = is.read(buff)) >= 0) {
206 fos.write(buff, 0, bread);
207 }
208 grokAssemblyExe = tempFile;
209
210 grokAssemblyExe.deleteOnExit();
211 LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
212 } catch (IOException ioe) {
213 this.setEnabled(false);
214 LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
215 throw new AnalysisException("Could not extract GrokAssembly.exe", ioe);
216 } finally {
217 if (fos != null) {
218 try {
219 fos.close();
220 } catch (Throwable e) {
221 LOGGER.debug("Error closing output stream");
222 }
223 }
224 if (is != null) {
225 try {
226 is.close();
227 } catch (Throwable e) {
228 LOGGER.debug("Error closing input stream");
229 }
230 }
231 }
232
233
234 final List<String> args = buildArgumentList();
235 BufferedReader rdr = null;
236 try {
237 final ProcessBuilder pb = new ProcessBuilder(args);
238 final Process p = pb.start();
239
240 rdr = new BufferedReader(new InputStreamReader(p.getErrorStream(), "UTF-8"));
241
242 while (rdr.ready() && rdr.readLine() != null) {
243
244 }
245
246 final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
247 final XPath xpath = XPathFactory.newInstance().newXPath();
248 final String error = xpath.evaluate("/assembly/error", doc);
249 if (p.waitFor() != 1 || error == null || error.isEmpty()) {
250 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
251 LOGGER.debug("GrokAssembly.exe is not working properly");
252 grokAssemblyExe = null;
253 this.setEnabled(false);
254 throw new AnalysisException("Could not execute .NET AssemblyAnalyzer");
255 }
256 } catch (Throwable e) {
257 if (e instanceof AnalysisException) {
258 throw (AnalysisException) e;
259 } else {
260 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
261 + "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
262 LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
263 this.setEnabled(false);
264 throw new AnalysisException("An error occured with the .NET AssemblyAnalyzer", e);
265 }
266 } finally {
267 if (rdr != null) {
268 try {
269 rdr.close();
270 } catch (IOException ex) {
271 LOGGER.trace("ignore", ex);
272 }
273 }
274 }
275 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
276 }
277
278
279
280
281
282
283 @Override
284 public void close() throws Exception {
285 super.close();
286 try {
287 if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
288 grokAssemblyExe.deleteOnExit();
289 }
290 } catch (SecurityException se) {
291 LOGGER.debug("Can't delete temporary GrokAssembly.exe");
292 }
293 }
294
295
296
297
298 private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(
299 SUPPORTED_EXTENSIONS).build();
300
301 @Override
302 protected FileFilter getFileFilter() {
303 return FILTER;
304 }
305
306
307
308
309
310
311 @Override
312 public String getName() {
313 return ANALYZER_NAME;
314 }
315
316
317
318
319
320
321 @Override
322 public AnalysisPhase getAnalysisPhase() {
323 return ANALYSIS_PHASE;
324 }
325
326
327
328
329
330
331 @Override
332 protected String getAnalyzerEnabledSettingKey() {
333 return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
334 }
335 }