Coverage Report - org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
AssemblyAnalyzer
62%
77/123
55%
21/38
6.5
 
 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 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  
 
 49  
 /**
 50  
  * Analyzer for getting company, product, and version information from a .NET
 51  
  * assembly.
 52  
  *
 53  
  * @author colezlaw
 54  
  *
 55  
  */
 56  11
 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  1
     private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
 66  
     /**
 67  
      * The list of supported extensions
 68  
      */
 69  1
     private static final String[] SUPPORTED_EXTENSIONS = {"dll", "exe"};
 70  
     /**
 71  
      * The temp value for GrokAssembly.exe
 72  
      */
 73  11
     private File grokAssemblyExe = null;
 74  
     /**
 75  
      * The DocumentBuilder for parsing the XML
 76  
      */
 77  
     private DocumentBuilder builder;
 78  
     /**
 79  
      * Logger
 80  
      */
 81  1
     private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyAnalyzer.class);
 82  
 
 83  
     /**
 84  
      * Builds the beginnings of a List for ProcessBuilder
 85  
      *
 86  
      * @return the list of arguments to begin populating the ProcessBuilder
 87  
      */
 88  
     private List<String> buildArgumentList() {
 89  
         // Use file.separator as a wild guess as to whether this is Windows
 90  8
         final List<String> args = new ArrayList<String>();
 91  8
         if (!"\\".equals(System.getProperty("file.separator"))) {
 92  0
             if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
 93  0
                 args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
 94  
             } else {
 95  0
                 args.add("mono");
 96  
             }
 97  
         }
 98  8
         args.add(grokAssemblyExe.getPath());
 99  
 
 100  8
         return args;
 101  
     }
 102  
 
 103  
     /**
 104  
      * Performs the analysis on a single Dependency.
 105  
      *
 106  
      * @param dependency the dependency to analyze
 107  
      * @param engine the engine to perform the analysis under
 108  
      * @throws AnalysisException if anything goes sideways
 109  
      */
 110  
     @Override
 111  
     public void analyzeFileType(Dependency dependency, Engine engine)
 112  
             throws AnalysisException {
 113  3
         if (grokAssemblyExe == null) {
 114  0
             LOGGER.warn("GrokAssembly didn't get deployed");
 115  0
             return;
 116  
         }
 117  
 
 118  3
         final List<String> args = buildArgumentList();
 119  3
         args.add(dependency.getActualFilePath());
 120  3
         final ProcessBuilder pb = new ProcessBuilder(args);
 121  3
         Document doc = null;
 122  
         try {
 123  3
             final Process proc = pb.start();
 124  
 
 125  3
             doc = builder.parse(proc.getInputStream());
 126  
 
 127  
             // Try evacuating the error stream
 128  3
             final String errorStream = IOUtils.toString(proc.getErrorStream(), "UTF-8");
 129  3
             if (null != errorStream && !errorStream.isEmpty()) {
 130  0
                 LOGGER.warn("Error from GrokAssembly: {}", errorStream);
 131  
             }
 132  
 
 133  3
             int rc = 0;
 134  
             try {
 135  3
                 rc = proc.waitFor();
 136  0
             } catch (InterruptedException ie) {
 137  0
                 return;
 138  3
             }
 139  3
             if (rc == 3) {
 140  0
                 LOGGER.debug("{} is not a .NET assembly or executable and as such cannot be analyzed by dependency-check",
 141  0
                         dependency.getActualFilePath());
 142  0
                 return;
 143  3
             } else if (rc != 0) {
 144  1
                 LOGGER.warn("Return code {} from GrokAssembly", rc);
 145  
             }
 146  
 
 147  3
             final XPath xpath = XPathFactory.newInstance().newXPath();
 148  
 
 149  
             // First, see if there was an error
 150  3
             final String error = xpath.evaluate("/assembly/error", doc);
 151  3
             if (error != null && !error.isEmpty()) {
 152  1
                 throw new AnalysisException(error);
 153  
             }
 154  
 
 155  2
             final String version = xpath.evaluate("/assembly/version", doc);
 156  2
             if (version != null) {
 157  2
                 dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version",
 158  
                         version, Confidence.HIGHEST));
 159  
             }
 160  
 
 161  2
             final String vendor = xpath.evaluate("/assembly/company", doc);
 162  2
             if (vendor != null) {
 163  2
                 dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor",
 164  
                         vendor, Confidence.HIGH));
 165  
             }
 166  
 
 167  2
             final String product = xpath.evaluate("/assembly/product", doc);
 168  2
             if (product != null) {
 169  2
                 dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product",
 170  
                         product, Confidence.HIGH));
 171  
             }
 172  
 
 173  0
         } catch (IOException ioe) {
 174  0
             throw new AnalysisException(ioe);
 175  0
         } catch (SAXException saxe) {
 176  0
             throw new AnalysisException("Couldn't parse GrokAssembly result", saxe);
 177  0
         } catch (XPathExpressionException xpe) {
 178  
             // This shouldn't happen
 179  0
             throw new AnalysisException(xpe);
 180  2
         }
 181  2
     }
 182  
 
 183  
     /**
 184  
      * Initialize the analyzer. In this case, extract GrokAssembly.exe to a
 185  
      * temporary location.
 186  
      *
 187  
      * @throws InitializationException thrown if anything goes wrong
 188  
      */
 189  
     @Override
 190  
     public void initializeFileTypeAnalyzer() throws InitializationException {
 191  
         final File tempFile;
 192  
         try {
 193  5
             tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
 194  0
         } catch (IOException ex) {
 195  0
             setEnabled(false);
 196  0
             throw new InitializationException("Unable to create temporary file for the assembly analyzerr", ex);
 197  5
         }
 198  5
         FileOutputStream fos = null;
 199  5
         InputStream is = null;
 200  
         try {
 201  5
             fos = new FileOutputStream(tempFile);
 202  5
             is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
 203  5
             IOUtils.copy(is, fos);
 204  
 
 205  5
             grokAssemblyExe = tempFile;
 206  
             // Set the temp file to get deleted when we're done
 207  5
             grokAssemblyExe.deleteOnExit();
 208  5
             LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
 209  0
         } catch (IOException ioe) {
 210  0
             this.setEnabled(false);
 211  0
             LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
 212  0
             throw new InitializationException("Could not extract GrokAssembly.exe", ioe);
 213  
         } finally {
 214  5
             if (fos != null) {
 215  
                 try {
 216  5
                     fos.close();
 217  0
                 } catch (Throwable e) {
 218  0
                     LOGGER.debug("Error closing output stream");
 219  5
                 }
 220  
             }
 221  5
             if (is != null) {
 222  
                 try {
 223  5
                     is.close();
 224  0
                 } catch (Throwable e) {
 225  0
                     LOGGER.debug("Error closing input stream");
 226  5
                 }
 227  
             }
 228  
         }
 229  
 
 230  
         // Now, need to see if GrokAssembly actually runs from this location.
 231  5
         final List<String> args = buildArgumentList();
 232  
         try {
 233  5
             final ProcessBuilder pb = new ProcessBuilder(args);
 234  5
             final Process p = pb.start();
 235  
             // Try evacuating the error stream
 236  5
             IOUtils.copy(p.getErrorStream(), NullOutputStream.NULL_OUTPUT_STREAM);
 237  
 
 238  5
             final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
 239  5
             final XPath xpath = XPathFactory.newInstance().newXPath();
 240  5
             final String error = xpath.evaluate("/assembly/error", doc);
 241  5
             if (p.waitFor() != 1 || error == null || error.isEmpty()) {
 242  0
                 LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
 243  0
                 LOGGER.debug("GrokAssembly.exe is not working properly");
 244  0
                 grokAssemblyExe = null;
 245  0
                 setEnabled(false);
 246  0
                 throw new InitializationException("Could not execute .NET AssemblyAnalyzer");
 247  
             }
 248  0
         } catch (InitializationException e) {
 249  0
             throw e;
 250  0
         } catch (Throwable e) {
 251  0
             LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
 252  
                     + "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
 253  0
             LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
 254  0
             setEnabled(false);
 255  0
             throw new InitializationException("An error occurred with the .NET AssemblyAnalyzer", e);
 256  5
         }
 257  
         try {
 258  5
             builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 259  0
         } catch (ParserConfigurationException ex) {
 260  0
             setEnabled(false);
 261  0
             throw new InitializationException("Error initializing the assembly analyzer", ex);
 262  5
         }
 263  5
     }
 264  
 
 265  
     /**
 266  
      * Removes resources used from the local file system.
 267  
      *
 268  
      * @throws Exception thrown if there is a problem closing the analyzer
 269  
      */
 270  
     @Override
 271  
     public void close() throws Exception {
 272  7
         super.close();
 273  
         try {
 274  7
             if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
 275  0
                 grokAssemblyExe.deleteOnExit();
 276  
             }
 277  0
         } catch (SecurityException se) {
 278  0
             LOGGER.debug("Can't delete temporary GrokAssembly.exe");
 279  7
         }
 280  7
     }
 281  
 
 282  
     /**
 283  
      * The File Filter used to filter supported extensions.
 284  
      */
 285  2
     private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(
 286  1
             SUPPORTED_EXTENSIONS).build();
 287  
 
 288  
     @Override
 289  
     protected FileFilter getFileFilter() {
 290  15
         return FILTER;
 291  
     }
 292  
 
 293  
     /**
 294  
      * Gets this analyzer's name.
 295  
      *
 296  
      * @return the analyzer name
 297  
      */
 298  
     @Override
 299  
     public String getName() {
 300  17
         return ANALYZER_NAME;
 301  
     }
 302  
 
 303  
     /**
 304  
      * Returns the phase this analyzer runs under.
 305  
      *
 306  
      * @return the phase this runs under
 307  
      */
 308  
     @Override
 309  
     public AnalysisPhase getAnalysisPhase() {
 310  4
         return ANALYSIS_PHASE;
 311  
     }
 312  
 
 313  
     /**
 314  
      * Returns the key used in the properties file to reference the analyzer's
 315  
      * enabled property.
 316  
      *
 317  
      * @return the analyzer's enabled property setting key
 318  
      */
 319  
     @Override
 320  
     protected String getAnalyzerEnabledSettingKey() {
 321  11
         return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
 322  
     }
 323  
 }