Coverage Report - org.owasp.dependencycheck.Engine
 
Classes in this File Line Coverage Branch Coverage Complexity
Engine
62%
87/140
71%
46/64
3.412
 
 1  
 /*
 2  
  * This file is part of dependency-check-core.
 3  
  *
 4  
  * Dependency-check-core is free software: you can redistribute it and/or modify it
 5  
  * under the terms of the GNU General Public License as published by the Free
 6  
  * Software Foundation, either version 3 of the License, or (at your option) any
 7  
  * later version.
 8  
  *
 9  
  * Dependency-check-core is distributed in the hope that it will be useful, but
 10  
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  
  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 12  
  * details.
 13  
  *
 14  
  * You should have received a copy of the GNU General Public License along with
 15  
  * dependency-check-core. If not, see http://www.gnu.org/licenses/.
 16  
  *
 17  
  * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
 18  
  */
 19  
 package org.owasp.dependencycheck;
 20  
 
 21  
 import java.util.EnumMap;
 22  
 import java.io.File;
 23  
 import java.util.ArrayList;
 24  
 import java.util.HashSet;
 25  
 import java.util.Iterator;
 26  
 import java.util.List;
 27  
 import java.util.Set;
 28  
 import java.util.logging.Level;
 29  
 import java.util.logging.Logger;
 30  
 import org.owasp.dependencycheck.analyzer.AnalysisException;
 31  
 import org.owasp.dependencycheck.analyzer.AnalysisPhase;
 32  
 import org.owasp.dependencycheck.analyzer.Analyzer;
 33  
 import org.owasp.dependencycheck.analyzer.AnalyzerService;
 34  
 import org.owasp.dependencycheck.data.CachedWebDataSource;
 35  
 import org.owasp.dependencycheck.data.UpdateException;
 36  
 import org.owasp.dependencycheck.data.UpdateService;
 37  
 import org.owasp.dependencycheck.dependency.Dependency;
 38  
 import org.owasp.dependencycheck.utils.FileUtils;
 39  
 import org.owasp.dependencycheck.utils.InvalidSettingException;
 40  
 import org.owasp.dependencycheck.utils.Settings;
 41  
 
 42  
 /**
 43  
  * Scans files, directories, etc. for Dependencies. Analyzers are loaded and
 44  
  * used to process the files found by the scan, if a file is encountered and an
 45  
  * Analyzer is associated with the file type then the file is turned into a
 46  
  * dependency.
 47  
  *
 48  
  * @author Jeremy Long (jeremy.long@owasp.org)
 49  
  */
 50  
 public class Engine {
 51  
 
 52  
     /**
 53  
      * The list of dependencies.
 54  
      */
 55  3
     private final List<Dependency> dependencies = new ArrayList<Dependency>();
 56  
     /**
 57  
      * A Map of analyzers grouped by Analysis phase.
 58  
      */
 59  3
     private final EnumMap<AnalysisPhase, List<Analyzer>> analyzers =
 60  
             new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
 61  
     /**
 62  
      * A set of extensions supported by the analyzers.
 63  
      */
 64  3
     private final Set<String> extensions = new HashSet<String>();
 65  
 
 66  
     /**
 67  
      * Creates a new Engine.
 68  
      */
 69  3
     public Engine() {
 70  3
         boolean autoUpdate = true;
 71  
         try {
 72  3
             autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
 73  0
         } catch (InvalidSettingException ex) {
 74  0
             Logger.getLogger(Engine.class.getName()).log(Level.FINE, "Invalid setting for auto-update; using true.");
 75  3
         }
 76  3
         if (autoUpdate) {
 77  0
             doUpdates();
 78  
         }
 79  3
         loadAnalyzers();
 80  3
     }
 81  
 
 82  
     /**
 83  
      * Creates a new Engine.
 84  
      *
 85  
      * @param autoUpdate indicates whether or not data should be updated from
 86  
      * the Internet
 87  
      * @deprecated This function should no longer be used; the autoupdate flag
 88  
      * should be set using:
 89  
      * <code>Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, value);</code>
 90  
      */
 91  
     @Deprecated
 92  0
     public Engine(boolean autoUpdate) {
 93  0
         if (autoUpdate) {
 94  0
             doUpdates();
 95  
         }
 96  0
         loadAnalyzers();
 97  0
     }
 98  
 
 99  
     /**
 100  
      * Loads the analyzers specified in the configuration file (or system
 101  
      * properties).
 102  
      */
 103  
     private void loadAnalyzers() {
 104  
 
 105  30
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 106  27
             analyzers.put(phase, new ArrayList<Analyzer>());
 107  
         }
 108  
 
 109  3
         final AnalyzerService service = AnalyzerService.getInstance();
 110  3
         final Iterator<Analyzer> iterator = service.getAnalyzers();
 111  27
         while (iterator.hasNext()) {
 112  24
             final Analyzer a = iterator.next();
 113  24
             analyzers.get(a.getAnalysisPhase()).add(a);
 114  24
             if (a.getSupportedExtensions() != null) {
 115  6
                 extensions.addAll(a.getSupportedExtensions());
 116  
             }
 117  24
         }
 118  3
     }
 119  
 
 120  
     /**
 121  
      * Get the List of the analyzers for a specific phase of analysis.
 122  
      *
 123  
      * @param phase the phase to get the configured analyzers.
 124  
      * @return the analyzers loaded
 125  
      */
 126  
     public List<Analyzer> getAnalyzers(AnalysisPhase phase) {
 127  0
         return analyzers.get(phase);
 128  
     }
 129  
 
 130  
     /**
 131  
      * Get the dependencies identified.
 132  
      *
 133  
      * @return the dependencies identified
 134  
      */
 135  
     public List<Dependency> getDependencies() {
 136  17
         return dependencies;
 137  
     }
 138  
 
 139  
     /**
 140  
      * Scans an array of files or directories. If a directory is specified, it
 141  
      * will be scanned recursively. Any dependencies identified are added to the
 142  
      * dependency collection.
 143  
      *
 144  
      * @since v0.3.2.5
 145  
      *
 146  
      * @param paths an array of paths to files or directories to be analyzed.
 147  
      */
 148  
     public void scan(String[] paths) {
 149  0
         for (String path : paths) {
 150  0
             final File file = new File(path);
 151  0
             scan(file);
 152  
         }
 153  0
     }
 154  
 
 155  
     /**
 156  
      * Scans a given file or directory. If a directory is specified, it will be
 157  
      * scanned recursively. Any dependencies identified are added to the
 158  
      * dependency collection.
 159  
      *
 160  
      * @param path the path to a file or directory to be analyzed.
 161  
      */
 162  
     public void scan(String path) {
 163  0
         final File file = new File(path);
 164  0
         scan(file);
 165  0
     }
 166  
 
 167  
     /**
 168  
      * Scans an array of files or directories. If a directory is specified, it
 169  
      * will be scanned recursively. Any dependencies identified are added to the
 170  
      * dependency collection.
 171  
      *
 172  
      * @since v0.3.2.5
 173  
      *
 174  
      * @param files an array of paths to files or directories to be analyzed.
 175  
      */
 176  
     public void scan(File[] files) {
 177  0
         for (File file : files) {
 178  0
             scan(file);
 179  
         }
 180  0
     }
 181  
 
 182  
     /**
 183  
      * Scans a list of files or directories. If a directory is specified, it
 184  
      * will be scanned recursively. Any dependencies identified are added to the
 185  
      * dependency collection.
 186  
      *
 187  
      * @since v0.3.2.5
 188  
      *
 189  
      * @param files a set of paths to files or directories to be analyzed.
 190  
      */
 191  
     public void scan(Set<File> files) {
 192  0
         for (File file : files) {
 193  0
             scan(file);
 194  
         }
 195  0
     }
 196  
 
 197  
     /**
 198  
      * Scans a list of files or directories. If a directory is specified, it
 199  
      * will be scanned recursively. Any dependencies identified are added to the
 200  
      * dependency collection.
 201  
      *
 202  
      * @since v0.3.2.5
 203  
      *
 204  
      * @param files a set of paths to files or directories to be analyzed.
 205  
      */
 206  
     public void scan(List<File> files) {
 207  0
         for (File file : files) {
 208  0
             scan(file);
 209  
         }
 210  0
     }
 211  
 
 212  
     /**
 213  
      * Scans a given file or directory. If a directory is specified, it will be
 214  
      * scanned recursively. Any dependencies identified are added to the
 215  
      * dependency collection.
 216  
      *
 217  
      * @since v0.3.2.4
 218  
      *
 219  
      * @param file the path to a file or directory to be analyzed.
 220  
      */
 221  
     public void scan(File file) {
 222  6
         if (file.exists()) {
 223  6
             if (file.isDirectory()) {
 224  3
                 scanDirectory(file);
 225  
             } else {
 226  3
                 scanFile(file);
 227  
             }
 228  
         }
 229  6
     }
 230  
 
 231  
     /**
 232  
      * Recursively scans files and directories. Any dependencies identified are
 233  
      * added to the dependency collection.
 234  
      *
 235  
      * @param dir the directory to scan.
 236  
      */
 237  
     protected void scanDirectory(File dir) {
 238  28
         final File[] files = dir.listFiles();
 239  28
         if (files != null) {
 240  58
             for (File f : files) {
 241  30
                 if (f.isDirectory()) {
 242  25
                     scanDirectory(f);
 243  
                 } else {
 244  5
                     scanFile(f);
 245  
                 }
 246  
             }
 247  
         }
 248  28
     }
 249  
 
 250  
     /**
 251  
      * Scans a specified file. If a dependency is identified it is added to the
 252  
      * dependency collection.
 253  
      *
 254  
      * @param file The file to scan.
 255  
      */
 256  
     protected void scanFile(File file) {
 257  8
         if (!file.isFile()) {
 258  0
             final String msg = String.format("Path passed to scanFile(File) is not a file: %s. Skipping the file.", file.toString());
 259  0
             Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
 260  0
             return;
 261  
         }
 262  8
         final String fileName = file.getName();
 263  8
         final String extension = FileUtils.getFileExtension(fileName);
 264  8
         if (extension != null) {
 265  8
             if (extensions.contains(extension)) {
 266  8
                 final Dependency dependency = new Dependency(file);
 267  8
                 dependencies.add(dependency);
 268  8
             }
 269  
         } else {
 270  0
             final String msg = String.format("No file extension found on file '%s'. The file was not analyzed.",
 271  
                     file.toString());
 272  0
             Logger.getLogger(Engine.class.getName()).log(Level.FINEST, msg);
 273  
         }
 274  8
     }
 275  
 
 276  
     /**
 277  
      * Runs the analyzers against all of the dependencies.
 278  
      */
 279  
     public void analyzeDependencies() {
 280  
         //phase one initialize
 281  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 282  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 283  9
             for (Analyzer a : analyzerList) {
 284  
                 try {
 285  8
                     final String msg = String.format("Initializing %s", a.getName());
 286  8
                     Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
 287  8
                     a.initialize();
 288  0
                 } catch (Exception ex) {
 289  0
                     final String msg = String.format("Exception occurred initializing %s.", a.getName());
 290  0
                     Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, msg);
 291  0
                     Logger.getLogger(Engine.class.getName()).log(Level.INFO, null, ex);
 292  
                     try {
 293  0
                         a.close();
 294  0
                     } catch (Exception ex1) {
 295  0
                         Logger.getLogger(Engine.class.getName()).log(Level.FINEST, null, ex1);
 296  0
                     }
 297  16
                 }
 298  
             }
 299  
         }
 300  
 
 301  
         // analysis phases
 302  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 303  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 304  
 
 305  9
             for (Analyzer a : analyzerList) {
 306  
                 /* need to create a copy of the collection because some of the
 307  
                  * analyzers may modify it. This prevents ConcurrentModificationExceptions.
 308  
                  * This is okay for adds/deletes because it happens per analyzer.
 309  
                  */
 310  8
                 final String msg = String.format("Begin Analyzer '%s'", a.getName());
 311  8
                 Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
 312  8
                 final Set<Dependency> dependencySet = new HashSet<Dependency>();
 313  8
                 dependencySet.addAll(dependencies);
 314  8
                 for (Dependency d : dependencySet) {
 315  24
                     final String msgFile = String.format("Begin Analysis of '%s'", d.getActualFilePath());
 316  24
                     Logger.getLogger(Engine.class.getName()).log(Level.FINE, msgFile);
 317  24
                     if (a.supportsExtension(d.getFileExtension())) {
 318  
                         try {
 319  21
                             a.analyze(d, this);
 320  0
                         } catch (AnalysisException ex) {
 321  0
                             d.addAnalysisException(ex);
 322  21
                         }
 323  
                     }
 324  24
                 }
 325  8
             }
 326  
         }
 327  
 
 328  
         //close/cleanup
 329  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 330  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 331  9
             for (Analyzer a : analyzerList) {
 332  8
                 final String msg = String.format("Closing Analyzer '%s'", a.getName());
 333  8
                 Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
 334  
                 try {
 335  8
                     a.close();
 336  0
                 } catch (Exception ex) {
 337  0
                     Logger.getLogger(Engine.class.getName()).log(Level.FINEST, null, ex);
 338  8
                 }
 339  8
             }
 340  
         }
 341  1
     }
 342  
 
 343  
     /**
 344  
      * Cycles through the cached web data sources and calls update on all of
 345  
      * them.
 346  
      */
 347  
     private void doUpdates() {
 348  0
         final UpdateService service = UpdateService.getInstance();
 349  0
         final Iterator<CachedWebDataSource> iterator = service.getDataSources();
 350  0
         while (iterator.hasNext()) {
 351  0
             final CachedWebDataSource source = iterator.next();
 352  
             try {
 353  0
                 source.update();
 354  0
             } catch (UpdateException ex) {
 355  0
                 Logger.getLogger(Engine.class.getName()).log(Level.WARNING,
 356  
                         "Unable to update Cached Web DataSource, using local data instead. Results may not include recent vulnerabilities.");
 357  0
                 Logger.getLogger(Engine.class.getName()).log(Level.FINE,
 358  
                         String.format("Unable to update details for %s", source.getClass().getName()), ex);
 359  0
             }
 360  0
         }
 361  0
     }
 362  
 
 363  
     /**
 364  
      * Returns a full list of all of the analyzers. This is useful for reporting
 365  
      * which analyzers where used.
 366  
      *
 367  
      * @return a list of Analyzers
 368  
      */
 369  
     public List<Analyzer> getAnalyzers() {
 370  1
         final List<Analyzer> ret = new ArrayList<Analyzer>();
 371  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 372  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 373  9
             ret.addAll(analyzerList);
 374  
         }
 375  1
         return ret;
 376  
     }
 377  
 
 378  
     /**
 379  
      * Checks all analyzers to see if an extension is supported.
 380  
      *
 381  
      * @param ext a file extension
 382  
      * @return true or false depending on whether or not the file extension is
 383  
      * supported
 384  
      */
 385  
     public boolean supportsExtension(String ext) {
 386  138
         if (ext == null) {
 387  8
             return false;
 388  
         }
 389  1259
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 390  1134
             final List<Analyzer> analyzerList = analyzers.get(phase);
 391  1134
             for (Analyzer a : analyzerList) {
 392  1009
                 if (a.getSupportedExtensions() != null && a.supportsExtension(ext)) {
 393  5
                     return true;
 394  
                 }
 395  
             }
 396  
         }
 397  125
         return false;
 398  
     }
 399  
 }