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) 2013 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.List;
26  import java.util.Set;
27  import java.util.regex.Pattern;
28  import org.owasp.dependencycheck.exception.InitializationException;
29  import org.owasp.dependencycheck.xml.suppression.SuppressionParseException;
30  import org.owasp.dependencycheck.xml.suppression.SuppressionParser;
31  import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
32  import org.owasp.dependencycheck.utils.DownloadFailedException;
33  import org.owasp.dependencycheck.utils.Downloader;
34  import org.owasp.dependencycheck.utils.FileUtils;
35  import org.owasp.dependencycheck.utils.Settings;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  import org.xml.sax.SAXException;
39  
40  /**
41   * Abstract base suppression analyzer that contains methods for parsing the
42   * suppression xml file.
43   *
44   * @author Jeremy Long
45   */
46  public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
47  
48      /**
49       * The Logger for use throughout the class
50       */
51      private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSuppressionAnalyzer.class);
52  
53      //<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
54      /**
55       * Returns a list of file EXTENSIONS supported by this analyzer.
56       *
57       * @return a list of file EXTENSIONS supported by this analyzer.
58       */
59      public Set<String> getSupportedExtensions() {
60          return null;
61      }
62  
63      //</editor-fold>
64      /**
65       * The initialize method loads the suppression XML file.
66       *
67       * @throws InitializationException thrown if there is an exception
68       */
69      @Override
70      public void initializeAnalyzer() throws InitializationException {
71          try {
72              loadSuppressionData();
73          } catch (SuppressionParseException ex) {
74              throw new InitializationException("Error initializing the suppression analyzer", ex);
75          }
76      }
77  
78      /**
79       * The list of suppression rules
80       */
81      private List<SuppressionRule> rules;
82  
83      /**
84       * Get the value of rules.
85       *
86       * @return the value of rules
87       */
88      public List<SuppressionRule> getRules() {
89          return rules;
90      }
91  
92      /**
93       * Set the value of rules.
94       *
95       * @param rules new value of rules
96       */
97      public void setRules(List<SuppressionRule> rules) {
98          this.rules = rules;
99      }
100 
101     /**
102      * Loads the suppression rules file.
103      *
104      * @throws SuppressionParseException thrown if the XML cannot be parsed.
105      */
106     private void loadSuppressionData() throws SuppressionParseException {
107         final SuppressionParser parser = new SuppressionParser();
108         File file = null;
109         try {
110             final InputStream in = this.getClass().getClassLoader().getResourceAsStream("dependencycheck-base-suppression.xml");
111             rules = parser.parseSuppressionRules(in);
112         } catch (SAXException ex) {
113             throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
114         }
115         final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);
116         if (suppressionFilePath == null) {
117             return;
118         }
119         boolean deleteTempFile = false;
120         try {
121             final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE);
122             if (uriRx.matcher(suppressionFilePath).matches()) {
123                 deleteTempFile = true;
124                 file = FileUtils.getTempFile("suppression", "xml");
125                 final URL url = new URL(suppressionFilePath);
126                 try {
127                     Downloader.fetchFile(url, file, false);
128                 } catch (DownloadFailedException ex) {
129                     Downloader.fetchFile(url, file, true);
130                 }
131             } else {
132                 file = new File(suppressionFilePath);
133                 InputStream suppressionsFromClasspath = null;
134                 if (!file.exists()) {
135                     try {
136                         suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath);
137                         if (suppressionsFromClasspath != null) {
138                             deleteTempFile = true;
139                             file = FileUtils.getTempFile("suppression", "xml");
140                             try {
141                                 org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
142                             } catch (IOException ex) {
143                                 throwSuppressionParseException("Unable to locate suppressions file in classpath", ex);
144                             }
145                         }
146                     } finally {
147                         if (suppressionsFromClasspath != null) {
148                             try {
149                                 suppressionsFromClasspath.close();
150                             } catch (IOException ex) {
151                                 LOGGER.debug("Failed to close stream", ex);
152                             }
153                         }
154                     }
155                 }
156             }
157             if (file != null) {
158                 if (!file.exists()) {
159                     final String msg = String.format("Suppression file '%s' does not exists", file.getPath());
160                     LOGGER.warn(msg);
161                     throw new SuppressionParseException(msg);
162                 }
163                 try {
164                     rules.addAll(parser.parseSuppressionRules(file));
165                     LOGGER.debug("{} suppression rules were loaded.", rules.size());
166                 } catch (SuppressionParseException ex) {
167                     LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath());
168                     LOGGER.warn(ex.getMessage());
169                     throw ex;
170                 }
171             }
172         } catch (DownloadFailedException ex) {
173             throwSuppressionParseException("Unable to fetch the configured suppression file", ex);
174         } catch (MalformedURLException ex) {
175             throwSuppressionParseException("Configured suppression file has an invalid URL", ex);
176         } catch (SuppressionParseException ex) {
177             throw ex;
178         } catch (IOException ex) {
179             throwSuppressionParseException("Unable to create temp file for suppressions", ex);
180         } finally {
181             if (deleteTempFile && file != null) {
182                 FileUtils.delete(file);
183             }
184         }
185     }
186 
187     /**
188      * Utility method to throw parse exceptions.
189      *
190      * @param message the exception message
191      * @param exception the cause of the exception
192      * @throws SuppressionParseException throws the generated
193      * SuppressionParseException
194      */
195     private void throwSuppressionParseException(String message, Exception exception) throws SuppressionParseException {
196         LOGGER.warn(message);
197         LOGGER.debug("", exception);
198         throw new SuppressionParseException(message, exception);
199     }
200 }