View Javadoc
1   /*
2    * This file is part of dependency-check-maven.
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) 2014 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.maven;
19  
20  import java.util.List;
21  import java.util.logging.Logger;
22  import org.apache.maven.project.MavenProject;
23  import org.owasp.dependencycheck.analyzer.Analyzer;
24  import org.owasp.dependencycheck.analyzer.CPEAnalyzer;
25  import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer;
26  import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
27  import org.owasp.dependencycheck.utils.Settings;
28  
29  /**
30   * A modified version of the core engine specifically designed to persist some data between multiple executions of a multi-module
31   * Maven project.
32   *
33   * @author Jeremy Long
34   */
35  public class Engine extends org.owasp.dependencycheck.Engine {
36  
37      /**
38       * The logger.
39       */
40      private static final transient Logger LOGGER = Logger.getLogger(Engine.class.getName());
41      /**
42       * A key used to persist an object in the MavenProject.
43       */
44      private static final String CPE_ANALYZER_KEY = "dependency-check-CPEAnalyzer";
45      /**
46       * The current MavenProject.
47       */
48      private MavenProject currentProject;
49      /**
50       * The list of MavenProjects that are part of the current build.
51       */
52      private List<MavenProject> reactorProjects;
53      /**
54       * Key used in the MavenProject context values to note whether or not an update has been executed.
55       */
56      public static final String UPDATE_EXECUTED_FLAG = "dependency-check-update-executed";
57  
58      /**
59       * Creates a new Engine to perform anyalsis on dependencies.
60       *
61       * @param project the current Maven project
62       * @param reactorProjects the reactor projects for the current Maven execution
63       * @throws DatabaseException thrown if there is an issue connecting to the database
64       */
65      public Engine(MavenProject project, List<MavenProject> reactorProjects) throws DatabaseException {
66          this.currentProject = project;
67          this.reactorProjects = reactorProjects;
68          initializeEngine();
69      }
70  
71      /**
72       * Runs the analyzers against all of the dependencies.
73       */
74      @Override
75      public void analyzeDependencies() {
76          final MavenProject root = getExecutionRoot();
77          if (root != null) {
78              LOGGER.fine(String.format("Checking root project, %s, if updates have already been completed", root.getArtifactId()));
79          } else {
80              LOGGER.fine("Checking root project, null, if updates have already been completed");
81          }
82          if (root != null && root.getContextValue(UPDATE_EXECUTED_FLAG) != null) {
83              System.setProperty(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE.toString());
84          }
85          super.analyzeDependencies();
86          if (root != null) {
87              root.setContextValue(UPDATE_EXECUTED_FLAG, Boolean.TRUE);
88          }
89      }
90  
91      /**
92       * Runs the update steps of dependency-check.
93       */
94      public void update() {
95          final MavenProject root = getExecutionRoot();
96          if (root != null && root.getContextValue(UPDATE_EXECUTED_FLAG) != null) {
97              System.setProperty(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE.toString());
98          }
99          this.doUpdates();
100     }
101 
102     /**
103      * This constructor should not be called. Use Engine(MavenProject) instead.
104      *
105      * @throws DatabaseException thrown if there is an issue connecting to the database
106      */
107     private Engine() throws DatabaseException {
108     }
109 
110     /**
111      * Initializes the given analyzer. This skips the initialization of the CPEAnalyzer if it has been initialized by a previous
112      * execution.
113      *
114      * @param analyzer the analyzer to initialize
115      * @return the initialized analyzer
116      */
117     @Override
118     protected Analyzer initializeAnalyzer(Analyzer analyzer) {
119         if ((analyzer instanceof CPEAnalyzer)) {
120             CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer();
121             if (cpe != null && cpe.isOpen()) {
122                 return cpe;
123             }
124             cpe = (CPEAnalyzer) super.initializeAnalyzer(analyzer);
125             storeCPEAnalyzer(cpe);
126         }
127         return super.initializeAnalyzer(analyzer);
128     }
129 
130     /**
131      * Releases resources used by the analyzers by calling close() on each analyzer.
132      */
133     @Override
134     public void cleanup() {
135         super.cleanup();
136         if (currentProject == null || reactorProjects == null) {
137             return;
138         }
139         if (this.currentProject == reactorProjects.get(reactorProjects.size() - 1)) {
140             final CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer();
141             if (cpe != null) {
142                 cpe.close();
143             }
144         }
145     }
146 
147     /**
148      * Closes the given analyzer. This skips closing the CPEAnalyzer.
149      *
150      * @param analyzer the analyzer to close
151      */
152     @Override
153     protected void closeAnalyzer(Analyzer analyzer) {
154         if ((analyzer instanceof CPEAnalyzer)) {
155             if (getPreviouslyLoadedCPEAnalyzer() == null) {
156                 super.closeAnalyzer(analyzer);
157             }
158         } else {
159             super.closeAnalyzer(analyzer);
160         }
161     }
162 
163     /**
164      * Gets the CPEAnalyzer from the root Maven Project.
165      *
166      * @return an initialized CPEAnalyzer
167      */
168     private CPEAnalyzer getPreviouslyLoadedCPEAnalyzer() {
169         CPEAnalyzer cpe = null;
170         final MavenProject project = getExecutionRoot();
171         if (project != null) {
172             final Object obj = project.getContextValue(CPE_ANALYZER_KEY);
173             if (obj != null && obj instanceof CPEAnalyzer) {
174                 cpe = (CPEAnalyzer) project.getContextValue(CPE_ANALYZER_KEY);
175             }
176         }
177         return cpe;
178     }
179 
180     /**
181      * Stores a CPEAnalyzer in the root Maven Project.
182      *
183      * @param cpe the CPEAnalyzer to store
184      */
185     private void storeCPEAnalyzer(CPEAnalyzer cpe) {
186         final MavenProject p = getExecutionRoot();
187         if (p != null) {
188             p.setContextValue(CPE_ANALYZER_KEY, cpe);
189         }
190     }
191 
192     /**
193      * Returns the root Maven Project.
194      *
195      * @return the root Maven Project
196      */
197     private MavenProject getExecutionRoot() {
198         if (reactorProjects == null) {
199             return null;
200         }
201         for (MavenProject p : reactorProjects) {
202             if (p.isExecutionRoot()) {
203                 return p;
204             }
205         }
206         //the following should  never run, but leaving it as a failsafe.
207         if (this.currentProject == null) {
208             return null;
209         }
210         MavenProject p = this.currentProject;
211         while (p.getParent() != null) {
212             p = p.getParent();
213         }
214         return p;
215     }
216 
217     /**
218      * Resets the file type analyzers so that they can be re-used to scan additional directories. Without the reset the analyzer
219      * might be disabled because the first scan/analyze did not identify any files that could be processed by the analyzer.
220      */
221     public void resetFileTypeAnalyzers() {
222         for (FileTypeAnalyzer a : getFileTypeAnalyzers()) {
223             a.reset();
224         }
225     }
226 }