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