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