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
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 Document doc = null;
119 try {
120 final Process proc = pb.start();
121
122 doc = builder.parse(proc.getInputStream());
123
124
125 final String errorStream = IOUtils.toString(proc.getErrorStream(), "UTF-8");
126 if (null != errorStream && !errorStream.isEmpty()) {
127 LOGGER.warn("Error from GrokAssembly: {}", errorStream);
128 }
129
130 int rc = 0;
131 try {
132 rc = proc.waitFor();
133 } catch (InterruptedException ie) {
134 return;
135 }
136 if (rc == 3) {
137 LOGGER.debug("{} is not a .NET assembly or executable and as such cannot be analyzed by dependency-check",
138 dependency.getActualFilePath());
139 return;
140 } else if (rc != 0) {
141 LOGGER.warn("Return code {} from GrokAssembly", rc);
142 }
143
144 final XPath xpath = XPathFactory.newInstance().newXPath();
145
146
147 final String error = xpath.evaluate("/assembly/error", doc);
148 if (error != null && !error.isEmpty()) {
149 throw new AnalysisException(error);
150 }
151
152 final String version = xpath.evaluate("/assembly/version", doc);
153 if (version != null) {
154 dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version",
155 version, Confidence.HIGHEST));
156 }
157
158 final String vendor = xpath.evaluate("/assembly/company", doc);
159 if (vendor != null) {
160 dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor",
161 vendor, Confidence.HIGH));
162 }
163
164 final String product = xpath.evaluate("/assembly/product", doc);
165 if (product != null) {
166 dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product",
167 product, Confidence.HIGH));
168 }
169
170 } catch (IOException ioe) {
171 throw new AnalysisException(ioe);
172 } catch (SAXException saxe) {
173 throw new AnalysisException("Couldn't parse GrokAssembly result", saxe);
174 } catch (XPathExpressionException xpe) {
175
176 throw new AnalysisException(xpe);
177 }
178 }
179
180
181
182
183
184
185 @Override
186 public void initializeFileTypeAnalyzer() throws Exception {
187 final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
188 FileOutputStream fos = null;
189 InputStream is = null;
190 try {
191 fos = new FileOutputStream(tempFile);
192 is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
193 IOUtils.copy(is, fos);
194
195 grokAssemblyExe = tempFile;
196
197 grokAssemblyExe.deleteOnExit();
198 LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
199 } catch (IOException ioe) {
200 this.setEnabled(false);
201 LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
202 throw new AnalysisException("Could not extract GrokAssembly.exe", ioe);
203 } finally {
204 if (fos != null) {
205 try {
206 fos.close();
207 } catch (Throwable e) {
208 LOGGER.debug("Error closing output stream");
209 }
210 }
211 if (is != null) {
212 try {
213 is.close();
214 } catch (Throwable e) {
215 LOGGER.debug("Error closing input stream");
216 }
217 }
218 }
219
220
221 final List<String> args = buildArgumentList();
222 try {
223 final ProcessBuilder pb = new ProcessBuilder(args);
224 final Process p = pb.start();
225
226 IOUtils.copy(p.getErrorStream(), NullOutputStream.NULL_OUTPUT_STREAM);
227
228 final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
229 final XPath xpath = XPathFactory.newInstance().newXPath();
230 final String error = xpath.evaluate("/assembly/error", doc);
231 if (p.waitFor() != 1 || error == null || error.isEmpty()) {
232 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
233 LOGGER.debug("GrokAssembly.exe is not working properly");
234 grokAssemblyExe = null;
235 this.setEnabled(false);
236 throw new AnalysisException("Could not execute .NET AssemblyAnalyzer");
237 }
238 } catch (AnalysisException e) {
239 throw e;
240 } catch (Throwable e) {
241 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
242 + "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
243 LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
244 this.setEnabled(false);
245 throw new AnalysisException("An error occurred with the .NET AssemblyAnalyzer", e);
246 }
247 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
248 }
249
250
251
252
253
254
255 @Override
256 public void close() throws Exception {
257 super.close();
258 try {
259 if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
260 grokAssemblyExe.deleteOnExit();
261 }
262 } catch (SecurityException se) {
263 LOGGER.debug("Can't delete temporary GrokAssembly.exe");
264 }
265 }
266
267
268
269
270 private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(
271 SUPPORTED_EXTENSIONS).build();
272
273 @Override
274 protected FileFilter getFileFilter() {
275 return FILTER;
276 }
277
278
279
280
281
282
283 @Override
284 public String getName() {
285 return ANALYZER_NAME;
286 }
287
288
289
290
291
292
293 @Override
294 public AnalysisPhase getAnalysisPhase() {
295 return ANALYSIS_PHASE;
296 }
297
298
299
300
301
302
303 @Override
304 protected String getAnalyzerEnabledSettingKey() {
305 return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
306 }
307 }