Coverage Report - org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
AssemblyAnalyzer
66%
82/123
41%
26/62
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 java.io.BufferedReader;
 21  
 import java.io.File;
 22  
 import java.io.FileOutputStream;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.io.InputStreamReader;
 26  
 import java.util.ArrayList;
 27  
 import java.util.List;
 28  
 import java.util.Set;
 29  
 import java.util.logging.Level;
 30  
 import java.util.logging.Logger;
 31  
 import javax.xml.parsers.DocumentBuilder;
 32  
 import javax.xml.parsers.DocumentBuilderFactory;
 33  
 import javax.xml.xpath.XPath;
 34  
 import javax.xml.xpath.XPathExpressionException;
 35  
 import javax.xml.xpath.XPathFactory;
 36  
 import org.owasp.dependencycheck.Engine;
 37  
 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
 38  
 import org.owasp.dependencycheck.dependency.Confidence;
 39  
 import org.owasp.dependencycheck.dependency.Dependency;
 40  
 import org.owasp.dependencycheck.dependency.Evidence;
 41  
 import org.owasp.dependencycheck.utils.Settings;
 42  
 import org.w3c.dom.Document;
 43  
 import org.xml.sax.SAXException;
 44  
 
 45  
 /**
 46  
  * Analyzer for getting company, product, and version information from a .NET assembly.
 47  
  *
 48  
  * @author colezlaw
 49  
  *
 50  
  */
 51  
 public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
 52  
 
 53  
     /**
 54  
      * The analyzer name
 55  
      */
 56  
     private static final String ANALYZER_NAME = "Assembly Analyzer";
 57  
     /**
 58  
      * The analysis phase
 59  
      */
 60  1
     private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
 61  
     /**
 62  
      * The list of supported extensions
 63  
      */
 64  1
     private static final Set<String> SUPPORTED_EXTENSIONS = newHashSet("dll", "exe");
 65  
     /**
 66  
      * The temp value for GrokAssembly.exe
 67  
      */
 68  
     private File grokAssemblyExe = null;
 69  
     /**
 70  
      * The DocumentBuilder for parsing the XML
 71  
      */
 72  
     private DocumentBuilder builder;
 73  
     /**
 74  
      * Logger
 75  
      */
 76  1
     private static final Logger LOGGER = Logger.getLogger(AssemblyAnalyzer.class.getName(), "dependencycheck-resources");
 77  
 
 78  
     /**
 79  
      * Builds the beginnings of a List for ProcessBuilder
 80  
      *
 81  
      * @return the list of arguments to begin populating the ProcessBuilder
 82  
      */
 83  
     private List<String> buildArgumentList() {
 84  
         // Use file.separator as a wild guess as to whether this is Windows
 85  8
         final List<String> args = new ArrayList<String>();
 86  8
         if (!"\\".equals(System.getProperty("file.separator"))) {
 87  0
             if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
 88  0
                 args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
 89  
             } else {
 90  0
                 args.add("mono");
 91  
             }
 92  
         }
 93  8
         args.add(grokAssemblyExe.getPath());
 94  
 
 95  8
         return args;
 96  
     }
 97  
 
 98  
     /**
 99  
      * Performs the analysis on a single Dependency.
 100  
      *
 101  
      * @param dependency the dependency to analyze
 102  
      * @param engine the engine to perform the analysis under
 103  
      * @throws AnalysisException if anything goes sideways
 104  
      */
 105  
     @Override
 106  
     public void analyzeFileType(Dependency dependency, Engine engine)
 107  
             throws AnalysisException {
 108  3
         if (grokAssemblyExe == null) {
 109  0
             LOGGER.warning("analyzer.AssemblyAnalyzer.notdeployed");
 110  0
             return;
 111  
         }
 112  
 
 113  3
         final List<String> args = buildArgumentList();
 114  3
         args.add(dependency.getActualFilePath());
 115  3
         final ProcessBuilder pb = new ProcessBuilder(args);
 116  3
         BufferedReader rdr = null;
 117  3
         Document doc = null;
 118  
         try {
 119  3
             final Process proc = pb.start();
 120  
             // Try evacuating the error stream
 121  3
             rdr = new BufferedReader(new InputStreamReader(proc.getErrorStream(), "UTF-8"));
 122  3
             String line = null;
 123  
             // CheckStyle:VisibilityModifier OFF
 124  3
             while (rdr.ready() && (line = rdr.readLine()) != null) {
 125  0
                 LOGGER.log(Level.WARNING, "analyzer.AssemblyAnalyzer.grokassembly.stderr", line);
 126  
             }
 127  
             // CheckStyle:VisibilityModifier ON
 128  3
             int rc = 0;
 129  3
             doc = builder.parse(proc.getInputStream());
 130  
 
 131  
             try {
 132  3
                 rc = proc.waitFor();
 133  0
             } catch (InterruptedException ie) {
 134  
                 return;
 135  3
             }
 136  3
             if (rc == 3) {
 137  0
                 LOGGER.log(Level.FINE, "analyzer.AssemblyAnalyzer.notassembly", dependency.getActualFilePath());
 138  
                 return;
 139  3
             } else if (rc != 0) {
 140  1
                 LOGGER.log(Level.WARNING, "analyzer.AssemblyAnalyzer.grokassembly.rc", rc);
 141  
             }
 142  
 
 143  3
             final XPath xpath = XPathFactory.newInstance().newXPath();
 144  
 
 145  
             // First, see if there was an error
 146  3
             final String error = xpath.evaluate("/assembly/error", doc);
 147  3
             if (error != null && !"".equals(error)) {
 148  1
                 throw new AnalysisException(error);
 149  
             }
 150  
 
 151  2
             final String version = xpath.evaluate("/assembly/version", doc);
 152  2
             if (version != null) {
 153  2
                 dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version",
 154  
                         version, Confidence.HIGHEST));
 155  
             }
 156  
 
 157  2
             final String vendor = xpath.evaluate("/assembly/company", doc);
 158  2
             if (vendor != null) {
 159  2
                 dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor",
 160  
                         vendor, Confidence.HIGH));
 161  
             }
 162  
 
 163  2
             final String product = xpath.evaluate("/assembly/product", doc);
 164  2
             if (product != null) {
 165  2
                 dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product",
 166  
                         product, Confidence.HIGH));
 167  
             }
 168  
 
 169  0
         } catch (IOException ioe) {
 170  0
             throw new AnalysisException(ioe);
 171  0
         } catch (SAXException saxe) {
 172  0
             throw new AnalysisException("Couldn't parse GrokAssembly result", saxe);
 173  0
         } catch (XPathExpressionException xpe) {
 174  
             // This shouldn't happen
 175  0
             throw new AnalysisException(xpe);
 176  
         } finally {
 177  3
             if (rdr != null) {
 178  
                 try {
 179  3
                     rdr.close();
 180  0
                 } catch (IOException ex) {
 181  0
                     LOGGER.log(Level.FINEST, "ignore", ex);
 182  4
                 }
 183  
             }
 184  
         }
 185  2
     }
 186  
 
 187  
     /**
 188  
      * Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location.
 189  
      *
 190  
      * @throws Exception if anything goes wrong
 191  
      */
 192  
     @Override
 193  
     public void initializeFileTypeAnalyzer() throws Exception {
 194  5
         final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
 195  5
         FileOutputStream fos = null;
 196  5
         InputStream is = null;
 197  
         try {
 198  5
             fos = new FileOutputStream(tempFile);
 199  5
             is = AssemblyAnalyzer.class.getClassLoader().getResourceAsStream("GrokAssembly.exe");
 200  5
             final byte[] buff = new byte[4096];
 201  5
             int bread = -1;
 202  15
             while ((bread = is.read(buff)) >= 0) {
 203  10
                 fos.write(buff, 0, bread);
 204  
             }
 205  5
             grokAssemblyExe = tempFile;
 206  
             // Set the temp file to get deleted when we're done
 207  5
             grokAssemblyExe.deleteOnExit();
 208  5
             LOGGER.log(Level.FINE, "analyzer.AssemblyAnalyzer.grokassembly.deployed", grokAssemblyExe.getPath());
 209  0
         } catch (IOException ioe) {
 210  0
             this.setEnabled(false);
 211  0
             LOGGER.log(Level.WARNING, "analyzer.AssemblyAnalyzer.grokassembly.notdeployed", ioe.getMessage());
 212  0
             throw new AnalysisException("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.fine("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.fine("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  5
         BufferedReader rdr = null;
 233  
         try {
 234  5
             final ProcessBuilder pb = new ProcessBuilder(args);
 235  5
             final Process p = pb.start();
 236  
             // Try evacuating the error stream
 237  5
             rdr = new BufferedReader(new InputStreamReader(p.getErrorStream(), "UTF-8"));
 238  
             // CheckStyle:VisibilityModifier OFF
 239  5
             while (rdr.ready() && rdr.readLine() != null) {
 240  
                 // We expect this to complain
 241  
             }
 242  
             // CheckStyle:VisibilityModifier ON
 243  5
             final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
 244  5
             final XPath xpath = XPathFactory.newInstance().newXPath();
 245  5
             final String error = xpath.evaluate("/assembly/error", doc);
 246  5
             if (p.waitFor() != 1 || error == null || "".equals(error)) {
 247  0
                 LOGGER.warning("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
 248  0
                 LOGGER.fine("GrokAssembly.exe is not working properly");
 249  0
                 grokAssemblyExe = null;
 250  0
                 this.setEnabled(false);
 251  0
                 throw new AnalysisException("Could not execute .NET AssemblyAnalyzer");
 252  
             }
 253  0
         } catch (Throwable e) {
 254  0
             if (e instanceof AnalysisException) {
 255  0
                 throw (AnalysisException) e;
 256  
             } else {
 257  0
                 LOGGER.warning("analyzer.AssemblyAnalyzer.grokassembly.initialization.failed");
 258  0
                 LOGGER.log(Level.FINE, "analyzer.AssemblyAnalyzer.grokassembly.initialization.message", e.getMessage());
 259  0
                 this.setEnabled(false);
 260  0
                 throw new AnalysisException("An error occured with the .NET AssemblyAnalyzer", e);
 261  
             }
 262  
         } finally {
 263  5
             if (rdr != null) {
 264  
                 try {
 265  5
                     rdr.close();
 266  0
                 } catch (IOException ex) {
 267  0
                     LOGGER.log(Level.FINEST, "ignore", ex);
 268  5
                 }
 269  
             }
 270  
         }
 271  5
         builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 272  5
     }
 273  
 
 274  
     @Override
 275  
     public void close() throws Exception {
 276  6
         super.close();
 277  
         try {
 278  6
             if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
 279  0
                 grokAssemblyExe.deleteOnExit();
 280  
             }
 281  0
         } catch (SecurityException se) {
 282  0
             LOGGER.fine("analyzer.AssemblyAnalyzer.grokassembly.notdeleted");
 283  6
         }
 284  6
     }
 285  
 
 286  
     /**
 287  
      * Gets the set of extensions supported by this analyzer.
 288  
      *
 289  
      * @return the list of supported extensions
 290  
      */
 291  
     @Override
 292  
     public Set<String> getSupportedExtensions() {
 293  853
         return SUPPORTED_EXTENSIONS;
 294  
     }
 295  
 
 296  
     /**
 297  
      * Gets this analyzer's name.
 298  
      *
 299  
      * @return the analyzer name
 300  
      */
 301  
     @Override
 302  
     public String getName() {
 303  5
         return ANALYZER_NAME;
 304  
     }
 305  
 
 306  
     /**
 307  
      * Returns the phase this analyzer runs under.
 308  
      *
 309  
      * @return the phase this runs under
 310  
      */
 311  
     @Override
 312  
     public AnalysisPhase getAnalysisPhase() {
 313  1
         return ANALYSIS_PHASE;
 314  
     }
 315  
 
 316  
     /**
 317  
      * Returns the key used in the properties file to reference the analyzer's enabled property.
 318  
      *
 319  
      * @return the analyzer's enabled property setting key
 320  
      */
 321  
     @Override
 322  
     protected String getAnalyzerEnabledSettingKey() {
 323  7
         return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
 324  
     }
 325  
 }