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.xml.suppression;
19  
20  import java.util.ArrayList;
21  import java.util.Calendar;
22  import java.util.List;
23  import javax.annotation.concurrent.NotThreadSafe;
24  import org.owasp.dependencycheck.exception.ParseException;
25  import org.owasp.dependencycheck.utils.DateUtil;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  import org.xml.sax.Attributes;
29  import org.xml.sax.SAXException;
30  import org.xml.sax.helpers.DefaultHandler;
31  
32  /**
33   * A handler to load suppression rules.
34   *
35   * @author Jeremy Long
36   */
37  @NotThreadSafe
38  public class SuppressionHandler extends DefaultHandler {
39  
40      /**
41       * The logger.
42       */
43      private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionHandler.class);
44  
45      /**
46       * The suppress node, indicates the start of a new rule.
47       */
48      public static final String SUPPRESS = "suppress";
49      /**
50       * The file path element name.
51       */
52      public static final String FILE_PATH = "filePath";
53      /**
54       * The sha1 hash element name.
55       */
56      public static final String SHA1 = "sha1";
57      /**
58       * The CVE element name.
59       */
60      public static final String CVE = "cve";
61      /**
62       * The vulnerabilityName element name.
63       */
64      public static final String VULNERABILITY_NAME = "vulnerabilityName";
65  
66      /**
67       * The CVE element name.
68       */
69      public static final String NOTES = "notes";
70  
71      /**
72       * The CPE element name.
73       */
74      public static final String CPE = "cpe";
75      /**
76       * The CWE element name.
77       */
78      public static final String CWE = "cwe";
79      /**
80       * The GAV element name.
81       */
82      public static final String GAV = "gav";
83      /**
84       * The Package URL element name.
85       */
86      public static final String PACKAGE_URL = "packageUrl";
87      /**
88       * The cvssBelow element name.
89       */
90      public static final String CVSS_BELOW = "cvssBelow";
91      /**
92       * A list of suppression rules.
93       */
94      private final List<SuppressionRule> suppressionRules = new ArrayList<>();
95      /**
96       * The current rule being read.
97       */
98      private SuppressionRule rule;
99      /**
100      * The attributes of the node being read.
101      */
102     private Attributes currentAttributes;
103     /**
104      * The current node text being extracted from the element.
105      */
106     private StringBuilder currentText;
107 
108     /**
109      * Get the value of suppressionRules.
110      *
111      * @return the value of suppressionRules
112      */
113     public List<SuppressionRule> getSuppressionRules() {
114         return suppressionRules;
115     }
116 
117     /**
118      * Handles the start element event.
119      *
120      * @param uri the URI of the element being processed
121      * @param localName the local name of the element being processed
122      * @param qName the qName of the element being processed
123      * @param attributes the attributes of the element being processed
124      * @throws SAXException thrown if there is an exception processing
125      */
126     @Override
127     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
128         currentAttributes = attributes;
129         currentText = new StringBuilder();
130         if (SUPPRESS.equals(qName)) {
131             rule = new SuppressionRule();
132             final String base = currentAttributes.getValue("base");
133             if (base != null) {
134                 rule.setBase(Boolean.parseBoolean(base));
135             } else {
136                 rule.setBase(false);
137             }
138             final String until = currentAttributes.getValue("until");
139             if (until != null) {
140                 try {
141                     rule.setUntil(DateUtil.parseXmlDate(until));
142                 } catch (ParseException ex) {
143                     throw new SAXException("Unable to parse until date in suppression file: " + until, ex);
144                 }
145             }
146         }
147     }
148 
149     /**
150      * Handles the end element event.
151      *
152      * @param uri the URI of the element
153      * @param localName the local name of the element
154      * @param qName the qName of the element
155      * @throws SAXException thrown if there is an exception processing
156      */
157     @Override
158     public void endElement(String uri, String localName, String qName) throws SAXException {
159         if (null != qName) {
160             switch (qName) {
161                 case SUPPRESS:
162                     if (rule.getUntil() != null && rule.getUntil().before(Calendar.getInstance())) {
163                         LOGGER.info("Suppression is expired for rule: {}", rule);
164                     } else {
165                         suppressionRules.add(rule);
166                     }
167                     rule = null;
168                     break;
169                 case FILE_PATH:
170                     rule.setFilePath(processPropertyType());
171                     break;
172                 case SHA1:
173                     rule.setSha1(currentText.toString().trim());
174                     break;
175                 case GAV:
176                     rule.setGav(processPropertyType());
177                     break;
178                 case PACKAGE_URL:
179                     rule.setPackageUrl(processPropertyType());
180                     break;
181                 case CPE:
182                     rule.addCpe(processPropertyType());
183                     break;
184                 case CWE:
185                     rule.addCwe(currentText.toString().trim());
186                     break;
187                 case CVE:
188                     rule.addCve(currentText.toString().trim());
189                     break;
190                 case VULNERABILITY_NAME:
191                     rule.addVulnerabilityName(processPropertyType());
192                     break;
193                 case NOTES:
194                     rule.setNotes(currentText.toString().trim());
195                     break;
196                 case CVSS_BELOW:
197                     final Double cvss = Double.valueOf(currentText.toString().trim());
198                     rule.addCvssBelow(cvss);
199                     break;
200                 default:
201                     break;
202             }
203         }
204     }
205 
206     /**
207      * Collects the body text of the node being processed.
208      *
209      * @param ch the char array of text
210      * @param start the start position to copy text from in the char array
211      * @param length the number of characters to copy from the char array
212      * @throws SAXException thrown if there is a parsing exception
213      */
214     @Override
215     public void characters(char[] ch, int start, int length) throws SAXException {
216         currentText.append(ch, start, length);
217     }
218 
219     /**
220      * Processes field members that have been collected during the characters
221      * and startElement method to construct a PropertyType object.
222      *
223      * @return a PropertyType object
224      */
225     private PropertyType processPropertyType() {
226         final PropertyType pt = new PropertyType();
227         pt.setValue(currentText.toString().trim());
228         if (currentAttributes != null && currentAttributes.getLength() > 0) {
229             final String regex = currentAttributes.getValue("regex");
230             if (regex != null) {
231                 pt.setRegex(Boolean.parseBoolean(regex));
232             }
233             final String caseSensitive = currentAttributes.getValue("caseSensitive");
234             if (caseSensitive != null) {
235                 pt.setCaseSensitive(Boolean.parseBoolean(caseSensitive));
236             }
237         }
238         return pt;
239     }
240 }