Coverage Report - org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
AssemblyAnalyzer
67%
86/127
50%
25/50
6.875
 
 1  
 /*
 2  
  * This file is part of dependency-check-core.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  *
 16  
  * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
 17  
  */
 18  
 package org.owasp.dependencycheck.analyzer;
 19  
 
 20  
 import ch.qos.cal10n.IMessageConveyor;
 21  
 import ch.qos.cal10n.MessageConveyor;
 22  
 import java.io.BufferedReader;
 23  
 import java.io.File;
 24  
 import java.io.FileFilter;
 25  
 import java.io.FileOutputStream;
 26  
 import java.io.IOException;
 27  
 import java.io.InputStream;
 28  
 import java.io.InputStreamReader;
 29  
 import org.owasp.dependencycheck.Engine;
 30  
 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
 31  
 import org.owasp.dependencycheck.dependency.Confidence;
 32  
 import org.owasp.dependencycheck.dependency.Dependency;
 33  
 import org.owasp.dependencycheck.dependency.Evidence;
 34  
 import org.owasp.dependencycheck.utils.FileFilterBuilder;
 35  
 import org.owasp.dependencycheck.utils.Settings;
 36  
 import org.slf4j.Logger;
 37  
 import org.slf4j.LoggerFactory;
 38  
 import org.w3c.dom.Document;
 39  
 import org.xml.sax.SAXException;
 40  
 
 41  
 import javax.xml.parsers.DocumentBuilder;
 42  
 import javax.xml.parsers.DocumentBuilderFactory;
 43  
 import javax.xml.xpath.XPath;
 44  
 import javax.xml.xpath.XPathExpressionException;
 45  
 import javax.xml.xpath.XPathFactory;
 46  
 import java.util.ArrayList;
 47  
 import java.util.List;
 48  
 import java.util.Locale;
 49  
 
 50  
 /**
 51  
  * Analyzer for getting company, product, and version information from a .NET assembly.
 52  
  *
 53  
  * @author colezlaw
 54  
  *
 55  
  */
 56  64
 public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
 57  
 
 58  
     /**
 59  
      * The analyzer name
 60  
      */
 61  
     private static final String ANALYZER_NAME = "Assembly Analyzer";
 62  
     /**
 63  
      * The analysis phase
 64  
      */
 65  8
     private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
 66  
     /**
 67  
      * The list of supported extensions
 68  
      */
 69  8
     private static final String[] SUPPORTED_EXTENSIONS = {"dll", "exe"};
 70  
     /**
 71  
      * The temp value for GrokAssembly.exe
 72  
      */
 73  64
     private File grokAssemblyExe = null;
 74  
     /**
 75  
      * The DocumentBuilder for parsing the XML
 76  
      */
 77  
     private DocumentBuilder builder;
 78  
     /**
 79  
      * Message Conveyer
 80  
      */
 81  8
     private static final IMessageConveyor MESSAGE_CONVERYOR = new MessageConveyor(Locale.getDefault());
 82  
     /**
 83  
      * Logger
 84  
      */
 85  8
     private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyAnalyzer.class);
 86  
 
 87  
     /**
 88  
      * Builds the beginnings of a List for ProcessBuilder
 89  
      *
 90  
      * @return the list of arguments to begin populating the ProcessBuilder
 91  
      */
 92  
     private List<String> buildArgumentList() {
 93  
         // Use file.separator as a wild guess as to whether this is Windows
 94  64
         final List<String> args = new ArrayList<String>();
 95  64
         if (!"\\".equals(System.getProperty("file.separator"))) {
 96  0
             if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
 97  0
                 args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
 98  
             } else {
 99  0
                 args.add("mono");
 100  
             }
 101  
         }
 102  64
         args.add(grokAssemblyExe.getPath());
 103  
 
 104  64
         return args;
 105  
     }
 106  
 
 107  
     /**
 108  
      * Performs the analysis on a single Dependency.
 109  
      *
 110  
      * @param dependency the dependency to analyze
 111  
      * @param engine the engine to perform the analysis under
 112  
      * @throws AnalysisException if anything goes sideways
 113  
      */
 114  
     @Override
 115  
     public void analyzeFileType(Dependency dependency, Engine engine)
 116  
             throws AnalysisException {
 117  24
         if (grokAssemblyExe == null) {
 118  0
             LOGGER.warn("GrokAssembly didn't get deployed");
 119  0
             return;
 120  
         }
 121  
 
 122  24
         final List<String> args = buildArgumentList();
 123  24
         args.add(dependency.getActualFilePath());
 124  24
         final ProcessBuilder pb = new ProcessBuilder(args);
 125  24
         BufferedReader rdr = null;
 126  24
         Document doc = null;
 127  
         try {
 128  24
             final Process proc = pb.start();
 129  
             // Try evacuating the error stream
 130  24
             rdr = new BufferedReader(new InputStreamReader(proc.getErrorStream(), "UTF-8"));
 131  24
             String line = null;
 132  
             // CHECKSTYLE:OFF
 133  24
             while (rdr.ready() && (line = rdr.readLine()) != null) {
 134  0
                 LOGGER.warn("Error from GrokAssembly: {}", line);
 135  
             }
 136  
             // CHECKSTYLE:ON
 137  24
             int rc = 0;
 138  24
             doc = builder.parse(proc.getInputStream());
 139  
 
 140  
             try {
 141  24
                 rc = proc.waitFor();
 142  0
             } catch (InterruptedException ie) {
 143  
                 return;
 144  24
             }
 145  24
             if (rc == 3) {
 146  0
                 LOGGER.debug("{} is not a .NET assembly or executable and as such cannot be analyzed by dependency-check",
 147  
                         dependency.getActualFilePath());
 148  
                 return;
 149  24
             } else if (rc != 0) {
 150  8
                 LOGGER.warn("Return code {} from GrokAssembly", rc);
 151  
             }
 152  
 
 153  24
             final XPath xpath = XPathFactory.newInstance().newXPath();
 154  
 
 155  
             // First, see if there was an error
 156  24
             final String error = xpath.evaluate("/assembly/error", doc);
 157  24
             if (error != null && !"".equals(error)) {
 158  8
                 throw new AnalysisException(error);
 159  
             }
 160  
 
 161  16
             final String version = xpath.evaluate("/assembly/version", doc);
 162  16
             if (version != null) {
 163  16
                 dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version",
 164  
                         version, Confidence.HIGHEST));
 165  
             }
 166  
 
 167  16
             final String vendor = xpath.evaluate("/assembly/company", doc);
 168  16
             if (vendor != null) {
 169  16
                 dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor",
 170  
                         vendor, Confidence.HIGH));
 171  
             }
 172  
 
 173  16
             final String product = xpath.evaluate("/assembly/product", doc);
 174  16
             if (product != null) {
 175  16
                 dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product",
 176  
                         product, Confidence.HIGH));
 177  
             }
 178  
 
 179  0
         } catch (IOException ioe) {
 180  0
             throw new AnalysisException(ioe);
 181  0
         } catch (SAXException saxe) {
 182  0
             throw new AnalysisException("Couldn't parse GrokAssembly result", saxe);
 183  0
         } catch (XPathExpressionException xpe) {
 184  
             // This shouldn't happen
 185  0
             throw new AnalysisException(xpe);
 186  
         } finally {
 187  24
             if (rdr != null) {
 188  
                 try {
 189  24
                     rdr.close();
 190  0
                 } catch (IOException ex) {
 191  0
                     LOGGER.debug("ignore", ex);
 192  32
                 }
 193  
             }
 194  
         }
 195  16
     }
 196  
 
 197  
     /**
 198  
      * Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location.
 199  
      *
 200  
      * @throws Exception if anything goes wrong
 201  
      */
 202  
     @Override
 203  
     public void initializeFileTypeAnalyzer() throws Exception {
 204  40
         final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
 205  40
         FileOutputStream fos = null;
 206  40
         InputStream is = null;
 207  
         try {
 208  40
             fos = new FileOutputStream(tempFile);
 209  40
             is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
 210  40
             final byte[] buff = new byte[4096];
 211  40
             int bread = -1;
 212  120
             while ((bread = is.read(buff)) >= 0) {
 213  80
                 fos.write(buff, 0, bread);
 214  
             }
 215  40
             grokAssemblyExe = tempFile;
 216  
             // Set the temp file to get deleted when we're done
 217  40
             grokAssemblyExe.deleteOnExit();
 218  40
             LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
 219  0
         } catch (IOException ioe) {
 220  0
             this.setEnabled(false);
 221  0
             LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
 222  0
             throw new AnalysisException("Could not extract GrokAssembly.exe", ioe);
 223  
         } finally {
 224  40
             if (fos != null) {
 225  
                 try {
 226  40
                     fos.close();
 227  0
                 } catch (Throwable e) {
 228  0
                     LOGGER.debug("Error closing output stream");
 229  40
                 }
 230  
             }
 231  40
             if (is != null) {
 232  
                 try {
 233  40
                     is.close();
 234  0
                 } catch (Throwable e) {
 235  0
                     LOGGER.debug("Error closing input stream");
 236  40
                 }
 237  
             }
 238  
         }
 239  
 
 240  
         // Now, need to see if GrokAssembly actually runs from this location.
 241  40
         final List<String> args = buildArgumentList();
 242  40
         BufferedReader rdr = null;
 243  
         try {
 244  40
             final ProcessBuilder pb = new ProcessBuilder(args);
 245  40
             final Process p = pb.start();
 246  
             // Try evacuating the error stream
 247  40
             rdr = new BufferedReader(new InputStreamReader(p.getErrorStream(), "UTF-8"));
 248  
             // CHECKSTYLE:OFF
 249  40
             while (rdr.ready() && rdr.readLine() != null) {
 250  
                 // We expect this to complain
 251  
             }
 252  
             // CHECKSTYLE:ON
 253  40
             final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
 254  40
             final XPath xpath = XPathFactory.newInstance().newXPath();
 255  40
             final String error = xpath.evaluate("/assembly/error", doc);
 256  40
             if (p.waitFor() != 1 || error == null || "".equals(error)) {
 257  0
                 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
 258  0
                 LOGGER.debug("GrokAssembly.exe is not working properly");
 259  0
                 grokAssemblyExe = null;
 260  0
                 this.setEnabled(false);
 261  0
                 throw new AnalysisException("Could not execute .NET AssemblyAnalyzer");
 262  
             }
 263  0
         } catch (Throwable e) {
 264  0
             if (e instanceof AnalysisException) {
 265  0
                 throw (AnalysisException) e;
 266  
             } else {
 267  0
                 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
 268  
                         + "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
 269  0
                 LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
 270  0
                 this.setEnabled(false);
 271  0
                 throw new AnalysisException("An error occured with the .NET AssemblyAnalyzer", e);
 272  
             }
 273  
         } finally {
 274  40
             if (rdr != null) {
 275  
                 try {
 276  40
                     rdr.close();
 277  0
                 } catch (IOException ex) {
 278  0
                     LOGGER.trace("ignore", ex);
 279  40
                 }
 280  
             }
 281  
         }
 282  40
         builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 283  40
     }
 284  
 
 285  
     /**
 286  
      * Removes resources used from the local file system.
 287  
      *
 288  
      * @throws Exception thrown if there is a problem closing the analyzer
 289  
      */
 290  
     @Override
 291  
     public void close() throws Exception {
 292  48
         super.close();
 293  
         try {
 294  48
             if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
 295  0
                 grokAssemblyExe.deleteOnExit();
 296  
             }
 297  0
         } catch (SecurityException se) {
 298  0
             LOGGER.debug("Can't delete temporary GrokAssembly.exe");
 299  48
         }
 300  48
     }
 301  
 
 302  
     /**
 303  
      * The File Filter used to filter supported extensions.
 304  
      */
 305  8
     private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(
 306  
             SUPPORTED_EXTENSIONS).build();
 307  
 
 308  
     @Override
 309  
     protected FileFilter getFileFilter() {
 310  6864
         return FILTER;
 311  
     }
 312  
 
 313  
     /**
 314  
      * Gets this analyzer's name.
 315  
      *
 316  
      * @return the analyzer name
 317  
      */
 318  
     @Override
 319  
     public String getName() {
 320  40
         return ANALYZER_NAME;
 321  
     }
 322  
 
 323  
     /**
 324  
      * Returns the phase this analyzer runs under.
 325  
      *
 326  
      * @return the phase this runs under
 327  
      */
 328  
     @Override
 329  
     public AnalysisPhase getAnalysisPhase() {
 330  16
         return ANALYSIS_PHASE;
 331  
     }
 332  
 
 333  
     /**
 334  
      * Returns the key used in the properties file to reference the analyzer's enabled property.
 335  
      *
 336  
      * @return the analyzer's enabled property setting key
 337  
      */
 338  
     @Override
 339  
     protected String getAnalyzerEnabledSettingKey() {
 340  64
         return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
 341  
     }
 342  
 }