View Javadoc
1   /*
2    * This file is part of dependency-check-core.
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) 2018 Nicolas Henneaux. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.analyzer;
19  
20  import org.owasp.dependencycheck.Engine;
21  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
22  import org.owasp.dependencycheck.data.artifactory.ArtifactorySearch;
23  import org.owasp.dependencycheck.data.nexus.MavenArtifact;
24  import org.owasp.dependencycheck.dependency.Confidence;
25  import org.owasp.dependencycheck.dependency.Dependency;
26  import org.owasp.dependencycheck.dependency.Evidence;
27  import org.owasp.dependencycheck.dependency.EvidenceType;
28  import org.owasp.dependencycheck.exception.InitializationException;
29  import org.owasp.dependencycheck.utils.DownloadFailedException;
30  import org.owasp.dependencycheck.utils.Downloader;
31  import org.owasp.dependencycheck.utils.FileFilterBuilder;
32  import org.owasp.dependencycheck.utils.FileUtils;
33  import org.owasp.dependencycheck.utils.InvalidSettingException;
34  import org.owasp.dependencycheck.utils.ResourceNotFoundException;
35  import org.owasp.dependencycheck.utils.Settings;
36  import org.owasp.dependencycheck.utils.TooManyRequestsException;
37  import org.owasp.dependencycheck.xml.pom.PomUtils;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import javax.annotation.concurrent.ThreadSafe;
42  import java.io.File;
43  import java.io.FileFilter;
44  import java.io.FileNotFoundException;
45  import java.io.IOException;
46  import java.net.URL;
47  import java.nio.file.Files;
48  import java.util.List;
49  
50  /**
51   * Analyzer which will attempt to locate a dependency, and the GAV information,
52   * by querying Artifactory for the dependency's hashes digest.
53   *
54   * @author nhenneaux
55   */
56  @ThreadSafe
57  public class ArtifactoryAnalyzer extends AbstractFileTypeAnalyzer {
58  
59      /**
60       * The logger.
61       */
62      private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactoryAnalyzer.class);
63  
64      /**
65       * The name of the analyzer.
66       */
67      private static final String ANALYZER_NAME = "Artifactory Analyzer";
68  
69      /**
70       * The phase in which this analyzer runs.
71       */
72      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
73  
74      /**
75       * The types of files on which this will work.
76       */
77      private static final String SUPPORTED_EXTENSIONS = "jar";
78  
79      /**
80       * The file filter used to determine which files this analyzer supports.
81       */
82      private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(SUPPORTED_EXTENSIONS).build();
83  
84      /**
85       * The searcher itself.
86       */
87      private ArtifactorySearch searcher;
88  
89      /**
90       * Initializes the analyzer with the configured settings.
91       *
92       * @param settings the configured settings to use
93       */
94      @Override
95      public synchronized void initialize(Settings settings) {
96          super.initialize(settings);
97          setEnabled(checkEnabled());
98      }
99  
100     /**
101      * Whether the analyzer is configured to support parallel processing.
102      *
103      * @return true if configured to support parallel processing; otherwise
104      * false
105      */
106     @Override
107     public boolean supportsParallelProcessing() {
108         return getSettings().getBoolean(Settings.KEYS.ANALYZER_ARTIFACTORY_PARALLEL_ANALYSIS, true);
109     }
110 
111     /**
112      * Determines if this analyzer is enabled.
113      *
114      * @return <code>true</code> if the analyzer is enabled; otherwise
115      * <code>false</code>
116      */
117     private boolean checkEnabled() {
118         try {
119             return getSettings().getBoolean(Settings.KEYS.ANALYZER_ARTIFACTORY_ENABLED);
120         } catch (InvalidSettingException ise) {
121             LOGGER.warn("Invalid setting. Disabling the Artifactory analyzer");
122         }
123         return false;
124     }
125 
126     /**
127      * Initializes the analyzer once before any analysis is performed.
128      *
129      * @param engine a reference to the dependency-check engine
130      * @throws InitializationException thrown when the analyzer is unable to
131      * connect to Artifactory
132      */
133     @Override
134     public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException {
135         LOGGER.debug("Initializing Artifactory analyzer");
136         final boolean enabled = isEnabled();
137         LOGGER.debug("Artifactory analyzer enabled: {}", enabled);
138         if (enabled) {
139             searcher = new ArtifactorySearch(getSettings());
140             final boolean preflightRequest = searcher.preflightRequest();
141             if (!preflightRequest) {
142                 setEnabled(false);
143                 throw new InitializationException("There was an issue connecting to Artifactory . Disabling analyzer.");
144             }
145         }
146     }
147 
148     /**
149      * Returns the analyzer's name.
150      *
151      * @return the name of the analyzer
152      */
153     @Override
154     public String getName() {
155         return ANALYZER_NAME;
156     }
157 
158     /**
159      * Returns the key used in the properties file to to reference the
160      * analyzer's enabled property.
161      *
162      * @return the analyzer's enabled property setting key.
163      */
164     @Override
165     protected String getAnalyzerEnabledSettingKey() {
166         return Settings.KEYS.ANALYZER_ARTIFACTORY_ENABLED;
167     }
168 
169     /**
170      * Returns the analysis phase under which the analyzer runs.
171      *
172      * @return the phase under which the analyzer runs
173      */
174     @Override
175     public AnalysisPhase getAnalysisPhase() {
176         return ANALYSIS_PHASE;
177     }
178 
179     @Override
180     protected FileFilter getFileFilter() {
181         return FILTER;
182     }
183 
184     /**
185      * Performs the analysis.
186      *
187      * @param dependency the dependency to analyze
188      * @param engine the engine
189      * @throws AnalysisException when there's an exception during analysis
190      */
191     @Override
192     public void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
193         for (Evidence e : dependency.getEvidence(EvidenceType.VENDOR)) {
194             if ("pom".equals(e.getSource())) {
195                 return;
196             }
197         }
198         try {
199             final List<MavenArtifact> mas = searcher.search(dependency);
200             final Confidence confidence = mas.size() > 1 ? Confidence.HIGH : Confidence.HIGHEST;
201             for (MavenArtifact ma : mas) {
202                 LOGGER.debug("Artifactory analyzer found artifact ({}) for dependency ({})", ma, dependency.getFileName());
203                 dependency.addAsEvidence("artifactory", ma, confidence);
204 
205                 if (ma.getPomUrl() != null) {
206                     processPom(dependency, ma);
207                 }
208             }
209         } catch (IllegalArgumentException iae) {
210             LOGGER.info("invalid sha1-hash on {}", dependency.getFileName());
211         } catch (FileNotFoundException fnfe) {
212             LOGGER.debug("Artifact not found in repository: '{}", dependency.getFileName());
213         } catch (IOException ioe) {
214             final String message = "Could not connect to Artifactory search. Analysis failed.";
215             LOGGER.error(message, ioe);
216             throw new AnalysisException(message, ioe);
217         }
218     }
219 
220     /**
221      * If necessary, downloads the pom.xml from Central and adds the evidence to
222      * the dependency.
223      *
224      * @param dependency the dependency to download and process the pom.xml
225      * @param ma the Maven artifact coordinates
226      * @throws IOException thrown if there is an I/O error
227      * @throws AnalysisException thrown if there is an error analyzing the pom
228      */
229     private void processPom(Dependency dependency, MavenArtifact ma) throws IOException, AnalysisException {
230         File pomFile = null;
231         try {
232             final File baseDir = getSettings().getTempDirectory();
233             pomFile = File.createTempFile("pom", ".xml", baseDir);
234             Files.delete(pomFile.toPath());
235             LOGGER.debug("Downloading {}", ma.getPomUrl());
236             //TODO add caching
237             final Downloader downloader = new Downloader(getSettings());
238             downloader.fetchFile(new URL(ma.getPomUrl()), pomFile,
239                     Settings.KEYS.ANALYZER_ARTIFACTORY_API_USERNAME,
240                     Settings.KEYS.ANALYZER_ARTIFACTORY_API_TOKEN);
241             PomUtils.analyzePOM(dependency, pomFile);
242 
243         } catch (DownloadFailedException ex) {
244             LOGGER.warn("Unable to download pom.xml for {} from Artifactory; "
245                     + "this could result in undetected CPE/CVEs.", dependency.getFileName());
246         } catch (TooManyRequestsException ex) {
247             this.setEnabled(false);
248             throw new AnalysisException("Received a 429 - too many requests from Artifactory; "
249                     + "the artifactory analyzer is being disabled.", ex);
250         } catch (ResourceNotFoundException ex) {
251             LOGGER.warn("pom.xml not found for {} from Artifactory; "
252                     + "this could result in undetected CPE/CVEs.", dependency.getFileName());
253         } finally {
254             if (pomFile != null && pomFile.exists() && !FileUtils.delete(pomFile)) {
255                 LOGGER.debug("Failed to delete temporary pom file {}", pomFile);
256                 pomFile.deleteOnExit();
257             }
258         }
259     }
260 
261 }