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