Coverage Report - org.owasp.dependencycheck.Engine
 
Classes in this File Line Coverage Branch Coverage Complexity
Engine
59%
77/130
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
                     a.initialize();
 286  0
                 } catch (Exception ex) {
 287  0
                     final String msg = String.format("\"Exception occurred initializing \"%s\".\"", a.getName());
 288  0
                     Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, msg);
 289  0
                     Logger.getLogger(Engine.class.getName()).log(Level.INFO, msg, ex);
 290  
                     try {
 291  0
                         a.close();
 292  0
                     } catch (Exception ex1) {
 293  0
                         Logger.getLogger(Engine.class.getName()).log(Level.FINEST, null, ex1);
 294  0
                     }
 295  16
                 }
 296  
             }
 297  
         }
 298  
 
 299  
         // analysis phases
 300  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 301  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 302  
 
 303  9
             for (Analyzer a : analyzerList) {
 304  
                 /* need to create a copy of the collection because some of the
 305  
                  * analyzers may modify it. This prevents ConcurrentModificationExceptions.
 306  
                  * This is okay for adds/deletes because it happens per analyzer.
 307  
                  */
 308  8
                 final Set<Dependency> dependencySet = new HashSet<Dependency>();
 309  8
                 dependencySet.addAll(dependencies);
 310  8
                 for (Dependency d : dependencySet) {
 311  24
                     if (a.supportsExtension(d.getFileExtension())) {
 312  
                         try {
 313  21
                             a.analyze(d, this);
 314  0
                         } catch (AnalysisException ex) {
 315  0
                             d.addAnalysisException(ex);
 316  45
                         }
 317  
                     }
 318  
                 }
 319  8
             }
 320  
         }
 321  
 
 322  
         //close/cleanup
 323  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 324  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 325  9
             for (Analyzer a : analyzerList) {
 326  
                 try {
 327  8
                     a.close();
 328  0
                 } catch (Exception ex) {
 329  0
                     Logger.getLogger(Engine.class.getName()).log(Level.FINEST, null, ex);
 330  16
                 }
 331  
             }
 332  
         }
 333  1
     }
 334  
 
 335  
     /**
 336  
      * Cycles through the cached web data sources and calls update on all of
 337  
      * them.
 338  
      */
 339  
     private void doUpdates() {
 340  0
         final UpdateService service = UpdateService.getInstance();
 341  0
         final Iterator<CachedWebDataSource> iterator = service.getDataSources();
 342  0
         while (iterator.hasNext()) {
 343  0
             final CachedWebDataSource source = iterator.next();
 344  
             try {
 345  0
                 source.update();
 346  0
             } catch (UpdateException ex) {
 347  0
                 Logger.getLogger(Engine.class.getName()).log(Level.WARNING,
 348  
                         "Unable to update Cached Web DataSource, using local data instead. Results may not include recent vulnerabilities.");
 349  0
                 Logger.getLogger(Engine.class.getName()).log(Level.FINE,
 350  
                         String.format("Unable to update details for %s", source.getClass().getName()), ex);
 351  0
             }
 352  0
         }
 353  0
     }
 354  
 
 355  
     /**
 356  
      * Returns a full list of all of the analyzers. This is useful for reporting
 357  
      * which analyzers where used.
 358  
      *
 359  
      * @return a list of Analyzers
 360  
      */
 361  
     public List<Analyzer> getAnalyzers() {
 362  1
         final List<Analyzer> ret = new ArrayList<Analyzer>();
 363  10
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 364  9
             final List<Analyzer> analyzerList = analyzers.get(phase);
 365  9
             ret.addAll(analyzerList);
 366  
         }
 367  1
         return ret;
 368  
     }
 369  
 
 370  
     /**
 371  
      * Checks all analyzers to see if an extension is supported.
 372  
      *
 373  
      * @param ext a file extension
 374  
      * @return true or false depending on whether or not the file extension is
 375  
      * supported
 376  
      */
 377  
     public boolean supportsExtension(String ext) {
 378  138
         if (ext == null) {
 379  8
             return false;
 380  
         }
 381  1259
         for (AnalysisPhase phase : AnalysisPhase.values()) {
 382  1134
             final List<Analyzer> analyzerList = analyzers.get(phase);
 383  1134
             for (Analyzer a : analyzerList) {
 384  1009
                 if (a.getSupportedExtensions() != null && a.supportsExtension(ext)) {
 385  5
                     return true;
 386  
                 }
 387  
             }
 388  
         }
 389  125
         return false;
 390  
     }
 391  
 }