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) 2012 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.analyzer;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.regex.Pattern;
29  import org.owasp.dependencycheck.Engine;
30  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
31  import org.owasp.dependencycheck.dependency.Dependency;
32  import org.owasp.dependencycheck.dependency.Evidence;
33  import org.owasp.dependencycheck.exception.InitializationException;
34  import org.owasp.dependencycheck.xml.suppression.PropertyType;
35  import org.owasp.dependencycheck.utils.DownloadFailedException;
36  import org.owasp.dependencycheck.utils.Downloader;
37  import org.owasp.dependencycheck.utils.FileUtils;
38  import org.owasp.dependencycheck.utils.Settings;
39  import org.owasp.dependencycheck.xml.hints.VendorDuplicatingHintRule;
40  import org.owasp.dependencycheck.xml.hints.HintParseException;
41  import org.owasp.dependencycheck.xml.hints.HintParser;
42  import org.owasp.dependencycheck.xml.hints.HintRule;
43  import org.owasp.dependencycheck.xml.hints.Hints;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  import org.xml.sax.SAXException;
47  
48  /**
49   * This analyzer adds evidence to dependencies to enhance the accuracy of
50   * library identification.
51   *
52   * @author Jeremy Long
53   */
54  public class HintAnalyzer extends AbstractAnalyzer {
55  
56      //<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
57      /**
58       * The name of the analyzer.
59       */
60      private static final String ANALYZER_NAME = "Hint Analyzer";
61      /**
62       * The phase that this analyzer is intended to run in.
63       */
64      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.PRE_IDENTIFIER_ANALYSIS;
65  
66      /**
67       * Returns the name of the analyzer.
68       *
69       * @return the name of the analyzer.
70       */
71      @Override
72      public String getName() {
73          return ANALYZER_NAME;
74      }
75  
76      /**
77       * Returns the phase that the analyzer is intended to run in.
78       *
79       * @return the phase that the analyzer is intended to run in.
80       */
81      @Override
82      public AnalysisPhase getAnalysisPhase() {
83          return ANALYSIS_PHASE;
84      }
85      /**
86       * <p>
87       * Returns the setting key to determine if the analyzer is enabled.</p>
88       *
89       * @return the key for the analyzer's enabled property
90       */
91      @Override
92      protected String getAnalyzerEnabledSettingKey() {
93          return Settings.KEYS.ANALYZER_HINT_ENABLED;
94      }
95  
96      /**
97       * The initialize method does nothing for this Analyzer.
98       *
99       * @throws InitializationException thrown if there is an exception
100      */
101     @Override
102     public void initializeAnalyzer() throws InitializationException {
103         try {
104             loadHintRules();
105         } catch (HintParseException ex) {
106             LOGGER.debug("Unable to parse hint file", ex);
107             throw new InitializationException("Unable to parse the hint file", ex);
108         }
109     }
110     //</editor-fold>
111 
112     /**
113      * The Logger for use throughout the class
114      */
115     private static final Logger LOGGER = LoggerFactory.getLogger(HintAnalyzer.class);
116     /**
117      * The name of the hint rule file
118      */
119     private static final String HINT_RULE_FILE_NAME = "dependencycheck-base-hint.xml";
120     /**
121      * The collection of hints.
122      */
123     private Hints hints;
124 
125     /**
126      * The HintAnalyzer uses knowledge about a dependency to add additional
127      * information to help in identification of identifiers or vulnerabilities.
128      *
129      * @param dependency The dependency being analyzed
130      * @param engine The scanning engine
131      * @throws AnalysisException is thrown if there is an exception analyzing
132      * the dependency.
133      */
134     @Override
135     protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
136         for (HintRule hint : hints.getHintRules()) {
137             boolean shouldAdd = false;
138             for (Evidence given : hint.getGivenVendor()) {
139                 if (dependency.getVendorEvidence().getEvidence().contains(given)) {
140                     shouldAdd = true;
141                     break;
142                 }
143             }
144             if (!shouldAdd) {
145                 for (Evidence given : hint.getGivenProduct()) {
146                     if (dependency.getProductEvidence().getEvidence().contains(given)) {
147                         shouldAdd = true;
148                         break;
149                     }
150                 }
151             }
152             if (!shouldAdd) {
153                 for (PropertyType pt : hint.getFilenames()) {
154                     if (pt.matches(dependency.getFileName())) {
155                         shouldAdd = true;
156                     }
157                 }
158             }
159             if (shouldAdd) {
160                 for (Evidence e : hint.getAddVendor()) {
161                     dependency.getVendorEvidence().addEvidence(e);
162                 }
163                 for (Evidence e : hint.getAddProduct()) {
164                     dependency.getProductEvidence().addEvidence(e);
165                 }
166                 for (Evidence e : hint.getAddVersion()) {
167                     dependency.getVersionEvidence().addEvidence(e);
168                 }
169             }
170         }
171 
172         final Iterator<Evidence> itr = dependency.getVendorEvidence().iterator();
173         final List<Evidence> newEntries = new ArrayList<Evidence>();
174         while (itr.hasNext()) {
175             final Evidence e = itr.next();
176             for (VendorDuplicatingHintRule dhr : hints.getVendorDuplicatingHintRules()) {
177                 if (dhr.getValue().equalsIgnoreCase(e.getValue(false))) {
178                     newEntries.add(new Evidence(e.getSource() + " (hint)",
179                             e.getName(), dhr.getDuplicate(), e.getConfidence()));
180                 }
181             }
182         }
183         for (Evidence e : newEntries) {
184             dependency.getVendorEvidence().addEvidence(e);
185         }
186 
187         //<editor-fold defaultstate="collapsed" desc="Old implementation">
188         /*
189         final Evidence springTest1 = new Evidence("Manifest",
190                 "Implementation-Title",
191                 "Spring Framework",
192                 Confidence.HIGH);
193 
194         final Evidence springTest2 = new Evidence("Manifest",
195                 "Implementation-Title",
196                 "org.springframework.core",
197                 Confidence.HIGH);
198 
199         final Evidence springTest3 = new Evidence("Manifest",
200                 "Implementation-Title",
201                 "spring-core",
202                 Confidence.HIGH);
203 
204         final Evidence springTest4 = new Evidence("jar",
205                 "package name",
206                 "springframework",
207                 Confidence.LOW);
208 
209         final Evidence springSecurityTest1 = new Evidence("Manifest",
210                 "Bundle-Name",
211                 "Spring Security Core",
212                 Confidence.MEDIUM);
213 
214         final Evidence springSecurityTest2 = new Evidence("pom",
215                 "artifactid",
216                 "spring-security-core",
217                 Confidence.HIGH);
218 
219         final Evidence symfony = new Evidence("composer.lock",
220             "vendor",
221             "symfony",
222             Confidence.HIGHEST);
223 
224         final Evidence zendframeworkVendor = new Evidence("composer.lock",
225             "vendor",
226             "zendframework",
227             Confidence.HIGHEST);
228 
229         final Evidence zendframeworkProduct = new Evidence("composer.lock",
230             "product",
231             "zendframework",
232             Confidence.HIGHEST);
233 
234         //springsource/vware problem
235         final Set<Evidence> product = dependency.getProductEvidence().getEvidence();
236         final Set<Evidence> vendor = dependency.getVendorEvidence().getEvidence();
237 
238         if (product.contains(springTest1) || product.contains(springTest2) || product.contains(springTest3)
239                 || (dependency.getFileName().contains("spring") && product.contains(springTest4))) {
240             dependency.getProductEvidence().addEvidence("hint analyzer", "product", "springsource spring framework", Confidence.HIGH);
241             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "SpringSource", Confidence.HIGH);
242             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "vmware", Confidence.HIGH);
243             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "pivotal", Confidence.HIGH);
244         }
245 
246         if (vendor.contains(springTest4)) {
247             dependency.getProductEvidence().addEvidence("hint analyzer", "product", "springsource_spring_framework", Confidence.HIGH);
248             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "vmware", Confidence.HIGH);
249             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "pivotal", Confidence.HIGH);
250         }
251 
252         if (product.contains(springSecurityTest1) || product.contains(springSecurityTest2)) {
253             dependency.getProductEvidence().addEvidence("hint analyzer", "product", "springsource_spring_security", Confidence.HIGH);
254             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "SpringSource", Confidence.HIGH);
255             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "vmware", Confidence.HIGH);
256         }
257 
258         if (vendor.contains(symfony)) {
259             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "sensiolabs", Confidence.HIGHEST);
260         }
261 
262         if (vendor.contains(zendframeworkVendor)) {
263             dependency.getVendorEvidence().addEvidence("hint analyzer", "vendor", "zend", Confidence.HIGHEST);
264         }
265 
266         if (product.contains(zendframeworkProduct)) {
267             dependency.getProductEvidence().addEvidence("hint analyzer", "vendor", "zend_framework", Confidence.HIGHEST);
268         }
269 
270         //sun/oracle problem
271         final Iterator<Evidence> itr = dependency.getVendorEvidence().iterator();
272         final List<Evidence> newEntries = new ArrayList<Evidence>();
273         while (itr.hasNext()) {
274             final Evidence e = itr.next();
275             if ("sun".equalsIgnoreCase(e.getValue(false))) {
276                 final Evidence newEvidence = new Evidence(e.getSource() + " (hint)", e.getName(), "oracle", e.getConfidence());
277                 newEntries.add(newEvidence);
278             } else if ("oracle".equalsIgnoreCase(e.getValue(false))) {
279                 final Evidence newEvidence = new Evidence(e.getSource() + " (hint)", e.getName(), "sun", e.getConfidence());
280                 newEntries.add(newEvidence);
281             }
282         }
283         for (Evidence e : newEntries) {
284             dependency.getVendorEvidence().addEvidence(e);
285         }
286          */
287         //</editor-fold>
288     }
289 
290     /**
291      * Loads the hint rules file.
292      *
293      * @throws HintParseException thrown if the XML cannot be parsed.
294      */
295     private void loadHintRules() throws HintParseException {
296         final HintParser parser = new HintParser();
297         File file = null;
298         try {
299             hints = parser.parseHints(this.getClass().getClassLoader().getResourceAsStream(HINT_RULE_FILE_NAME));
300         } catch (HintParseException ex) {
301             LOGGER.error("Unable to parse the base hint data file");
302             LOGGER.debug("Unable to parse the base hint data file", ex);
303         } catch (SAXException ex) {
304             LOGGER.error("Unable to parse the base hint data file");
305             LOGGER.debug("Unable to parse the base hint data file", ex);
306         }
307         final String filePath = Settings.getString(Settings.KEYS.HINTS_FILE);
308         if (filePath == null) {
309             return;
310         }
311         boolean deleteTempFile = false;
312         try {
313             final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE);
314             if (uriRx.matcher(filePath).matches()) {
315                 deleteTempFile = true;
316                 file = FileUtils.getTempFile("hint", "xml");
317                 final URL url = new URL(filePath);
318                 try {
319                     Downloader.fetchFile(url, file, false);
320                 } catch (DownloadFailedException ex) {
321                     Downloader.fetchFile(url, file, true);
322                 }
323             } else {
324                 file = new File(filePath);
325                 if (!file.exists()) {
326                     InputStream fromClasspath = null;
327                     try {
328                         fromClasspath = this.getClass().getClassLoader().getResourceAsStream(filePath);
329                         if (fromClasspath != null) {
330                             deleteTempFile = true;
331                             file = FileUtils.getTempFile("hint", "xml");
332                             try {
333                                 org.apache.commons.io.FileUtils.copyInputStreamToFile(fromClasspath, file);
334                             } catch (IOException ex) {
335                                 throw new HintParseException("Unable to locate hints file in classpath", ex);
336                             }
337                         }
338                     } finally {
339                         if (fromClasspath != null) {
340                             fromClasspath.close();
341                         }
342                     }
343                 }
344             }
345 
346             if (file != null) {
347                 try {
348                     final Hints newHints = parser.parseHints(file);
349                     hints.getHintRules().addAll(newHints.getHintRules());
350                     hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules());
351                     LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size());
352                     LOGGER.debug("{} duplicating hint rules were loaded.", hints.getVendorDuplicatingHintRules().size());
353                 } catch (HintParseException ex) {
354                     LOGGER.warn("Unable to parse hint rule xml file '{}'", file.getPath());
355                     LOGGER.warn(ex.getMessage());
356                     LOGGER.debug("", ex);
357                     throw ex;
358                 }
359             }
360         } catch (DownloadFailedException ex) {
361             throw new HintParseException("Unable to fetch the configured hint file", ex);
362         } catch (MalformedURLException ex) {
363             throw new HintParseException("Configured hint file has an invalid URL", ex);
364         } catch (IOException ex) {
365             throw new HintParseException("Unable to create temp file for hints", ex);
366         } finally {
367             if (deleteTempFile && file != null) {
368                 FileUtils.delete(file);
369             }
370         }
371     }
372 }