1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.xml.suppression;
19
20 import org.owasp.dependencycheck.exception.ParseException;
21 import org.owasp.dependencycheck.utils.DateUtil;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import org.xml.sax.Attributes;
25 import org.xml.sax.SAXException;
26 import org.xml.sax.helpers.DefaultHandler;
27
28 import javax.annotation.concurrent.NotThreadSafe;
29 import java.util.ArrayList;
30 import java.util.Calendar;
31 import java.util.List;
32 import java.util.Optional;
33
34
35
36
37
38
39
40 @NotThreadSafe
41 public class SuppressionHandler extends DefaultHandler {
42
43
44
45
46 private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionHandler.class);
47
48
49
50
51 public static final String SUPPRESSION_GROUP = "suppressionGroup";
52
53
54
55 public static final String SUPPRESS = "suppress";
56
57
58
59 public static final String FILE_PATH = "filePath";
60
61
62
63 public static final String SHA1 = "sha1";
64
65
66
67 public static final String CVE = "cve";
68
69
70
71 public static final String VULNERABILITY_NAME = "vulnerabilityName";
72
73
74
75
76 public static final String NOTES = "notes";
77
78
79
80
81 public static final String CPE = "cpe";
82
83
84
85 public static final String CWE = "cwe";
86
87
88
89 public static final String GAV = "gav";
90
91
92
93 public static final String PACKAGE_URL = "packageUrl";
94
95
96
97 public static final String CVSS_BELOW = "cvssBelow";
98
99
100
101 public static final String CVSS_V2_BELOW = "cvssV2Below";
102
103
104
105 public static final String CVSS_V3_BELOW = "cvssV3Below";
106
107
108
109 public static final String CVSS_V4_BELOW = "cvssV4Below";
110
111
112
113 private final List<SuppressionRule> suppressionRules = new ArrayList<>();
114
115
116
117 private SuppressionRule rule;
118
119
120
121 private Attributes currentAttributes;
122
123
124
125 private StringBuilder currentText;
126
127 private Boolean groupBase = null;
128 private Calendar groupUntil = null;
129
130
131
132
133
134
135
136 public List<SuppressionRule> getSuppressionRules() {
137 return suppressionRules;
138 }
139
140
141
142
143
144
145
146
147
148
149 @Override
150 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
151 currentAttributes = attributes;
152 currentText = new StringBuilder();
153
154 if (SUPPRESSION_GROUP.equals(qName)) {
155 groupBase = attributes.getValue("base") != null ? Boolean.parseBoolean(attributes.getValue("base")) : null;
156 groupUntil = parseUntilAttribute(attributes).orElse(null);
157 }
158
159 if (SUPPRESS.equals(qName)) {
160 Boolean base = attributes.getValue("base") != null ? Boolean.parseBoolean(attributes.getValue("base")) : null;
161 Calendar until = parseUntilAttribute(attributes).orElse(null);
162
163 rule = new SuppressionRule();
164
165 rule.setBase(base != null ? base : groupBase);
166 rule.setUntil(until != null ? until : groupUntil);
167 }
168 }
169
170
171
172
173
174
175
176
177 private static Optional<Calendar> parseUntilAttribute(Attributes attributes) throws SAXException {
178 String untilStr = attributes.getValue("until");
179 if (untilStr != null) {
180 try {
181 return Optional.of(DateUtil.parseXmlDate(untilStr));
182 } catch (ParseException ex) {
183 throw new SAXException("Unable to parse attribute 'until': " + untilStr, ex);
184 }
185 } else {
186 return Optional.empty();
187 }
188 }
189
190
191
192
193
194
195
196
197
198 @Override
199 public void endElement(String uri, String localName, String qName) throws SAXException {
200 if (null != qName) {
201 switch (qName) {
202 case SUPPRESS:
203 if (rule.getUntil() != null && rule.getUntil().before(Calendar.getInstance())) {
204 LOGGER.info("Suppression is expired for rule: {}", rule);
205 } else {
206 suppressionRules.add(rule);
207 }
208 rule = null;
209 break;
210 case SUPPRESSION_GROUP:
211 groupBase = null;
212 groupUntil = null;
213 break;
214 case FILE_PATH:
215 rule.setFilePath(processPropertyType());
216 break;
217 case SHA1:
218 rule.setSha1(currentText.toString().trim());
219 break;
220 case GAV:
221 rule.setGav(processPropertyType());
222 break;
223 case PACKAGE_URL:
224 rule.setPackageUrl(processPropertyType());
225 break;
226 case CPE:
227 rule.addCpe(processPropertyType());
228 break;
229 case CWE:
230 rule.addCwe(currentText.toString().trim());
231 break;
232 case CVE:
233 rule.addCve(currentText.toString().trim());
234 break;
235 case VULNERABILITY_NAME:
236 rule.addVulnerabilityName(processPropertyType());
237 break;
238 case NOTES:
239
240 if(rule != null) {
241 rule.setNotes(currentText.toString().trim());
242 }
243 break;
244 case CVSS_BELOW:
245 final Double cvss = Double.valueOf(currentText.toString().trim());
246 rule.addCvssBelow(cvss);
247 break;
248 case CVSS_V2_BELOW:
249 final Double cvssV2 = Double.valueOf(currentText.toString().trim());
250 rule.addCvssV2Below(cvssV2);
251 break;
252 case CVSS_V3_BELOW:
253 final Double cvssV3 = Double.valueOf(currentText.toString().trim());
254 rule.addCvssV3Below(cvssV3);
255 break;
256 case CVSS_V4_BELOW:
257 final Double cvssV4 = Double.valueOf(currentText.toString().trim());
258 rule.addCvssV4Below(cvssV4);
259 break;
260 default:
261 break;
262 }
263 }
264 }
265
266
267
268
269
270
271
272
273
274 @Override
275 public void characters(char[] ch, int start, int length) throws SAXException {
276 currentText.append(ch, start, length);
277 }
278
279
280
281
282
283
284
285 private PropertyType processPropertyType() {
286 boolean isRegex = false;
287 boolean isCaseSensitive = false;
288 if (currentAttributes != null && currentAttributes.getLength() > 0) {
289 final String regex = currentAttributes.getValue("regex");
290 if (regex != null) {
291 isRegex = Boolean.parseBoolean(regex);
292 }
293 final String caseSensitive = currentAttributes.getValue("caseSensitive");
294 if (caseSensitive != null) {
295 isCaseSensitive = Boolean.parseBoolean(caseSensitive);
296 }
297 }
298 return new PropertyType(currentText.toString().trim(), isRegex, isCaseSensitive);
299 }
300 }