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.suppression;
19
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23 import org.owasp.dependencycheck.dependency.Dependency;
24 import org.owasp.dependencycheck.dependency.Identifier;
25 import org.owasp.dependencycheck.dependency.Vulnerability;
26
27 /**
28 *
29 * @author Jeremy Long
30 */
31 public class SuppressionRule {
32
33 /**
34 * The file path for the suppression.
35 */
36 private PropertyType filePath;
37
38 /**
39 * Get the value of filePath.
40 *
41 * @return the value of filePath
42 */
43 public PropertyType getFilePath() {
44 return filePath;
45 }
46
47 /**
48 * Set the value of filePath.
49 *
50 * @param filePath new value of filePath
51 */
52 public void setFilePath(PropertyType filePath) {
53 this.filePath = filePath;
54 }
55 /**
56 * The sha1 hash.
57 */
58 private String sha1;
59
60 /**
61 * Get the value of sha1.
62 *
63 * @return the value of sha1
64 */
65 public String getSha1() {
66 return sha1;
67 }
68
69 /**
70 * Set the value of sha1.
71 *
72 * @param sha1 new value of sha1
73 */
74 public void setSha1(String sha1) {
75 this.sha1 = sha1;
76 }
77 /**
78 * A list of CPEs to suppression
79 */
80 private List<PropertyType> cpe = new ArrayList<PropertyType>();
81
82 /**
83 * Get the value of cpe.
84 *
85 * @return the value of cpe
86 */
87 public List<PropertyType> getCpe() {
88 return cpe;
89 }
90
91 /**
92 * Set the value of cpe.
93 *
94 * @param cpe new value of cpe
95 */
96 public void setCpe(List<PropertyType> cpe) {
97 this.cpe = cpe;
98 }
99
100 /**
101 * Adds the cpe to the cpe list.
102 *
103 * @param cpe the cpe to add
104 */
105 public void addCpe(PropertyType cpe) {
106 this.cpe.add(cpe);
107 }
108
109 /**
110 * Returns whether or not this suppression rule as CPE entries.
111 *
112 * @return whether or not this suppression rule as CPE entries
113 */
114 public boolean hasCpe() {
115 return !cpe.isEmpty();
116 }
117 /**
118 * The list of cvssBelow scores.
119 */
120 private List<Float> cvssBelow = new ArrayList<Float>();
121
122 /**
123 * Get the value of cvssBelow.
124 *
125 * @return the value of cvssBelow
126 */
127 public List<Float> getCvssBelow() {
128 return cvssBelow;
129 }
130
131 /**
132 * Set the value of cvssBelow.
133 *
134 * @param cvssBelow new value of cvssBelow
135 */
136 public void setCvssBelow(List<Float> cvssBelow) {
137 this.cvssBelow = cvssBelow;
138 }
139
140 /**
141 * Adds the cvss to the cvssBelow list.
142 *
143 * @param cvss the cvss to add
144 */
145 public void addCvssBelow(Float cvss) {
146 this.cvssBelow.add(cvss);
147 }
148
149 /**
150 * Returns whether or not this suppression rule has cvss suppressions.
151 *
152 * @return whether or not this suppression rule has cvss suppressions
153 */
154 public boolean hasCvssBelow() {
155 return !cvssBelow.isEmpty();
156 }
157 /**
158 * The list of cwe entries to suppress.
159 */
160 private List<String> cwe = new ArrayList<String>();
161
162 /**
163 * Get the value of cwe.
164 *
165 * @return the value of cwe
166 */
167 public List<String> getCwe() {
168 return cwe;
169 }
170
171 /**
172 * Set the value of cwe.
173 *
174 * @param cwe new value of cwe
175 */
176 public void setCwe(List<String> cwe) {
177 this.cwe = cwe;
178 }
179
180 /**
181 * Adds the cwe to the cwe list.
182 *
183 * @param cwe the cwe to add
184 */
185 public void addCwe(String cwe) {
186 this.cwe.add(cwe);
187 }
188
189 /**
190 * Returns whether this suppression rule has CWE entries.
191 *
192 * @return whether this suppression rule has CWE entries
193 */
194 public boolean hasCwe() {
195 return !cwe.isEmpty();
196 }
197 /**
198 * The list of cve entries to suppress.
199 */
200 private List<String> cve = new ArrayList<String>();
201
202 /**
203 * Get the value of cve.
204 *
205 * @return the value of cve
206 */
207 public List<String> getCve() {
208 return cve;
209 }
210
211 /**
212 * Set the value of cve.
213 *
214 * @param cve new value of cve
215 */
216 public void setCve(List<String> cve) {
217 this.cve = cve;
218 }
219
220 /**
221 * Adds the cve to the cve list.
222 *
223 * @param cve the cve to add
224 */
225 public void addCve(String cve) {
226 this.cve.add(cve);
227 }
228
229 /**
230 * Returns whether this suppression rule has CVE entries.
231 *
232 * @return whether this suppression rule has CVE entries
233 */
234 public boolean hasCve() {
235 return !cve.isEmpty();
236 }
237 /**
238 * A Maven GAV to suppression.
239 */
240 private PropertyType gav = null;
241
242 /**
243 * Get the value of Maven GAV.
244 *
245 * @return the value of gav
246 */
247 public PropertyType getGav() {
248 return gav;
249 }
250
251 /**
252 * Set the value of Maven GAV.
253 *
254 * @param gav new value of Maven gav
255 */
256 public void setGav(PropertyType gav) {
257 this.gav = gav;
258 }
259
260 /**
261 * Returns whether or not this suppression rule as GAV entries.
262 *
263 * @return whether or not this suppression rule as GAV entries
264 */
265 public boolean hasGav() {
266 return gav != null;
267 }
268
269 /**
270 * A flag indicating whether or not the suppression rule is a core/base rule that should not be included in the resulting
271 * report in the "suppressed" section.
272 */
273 private boolean base;
274
275 /**
276 * Get the value of base.
277 *
278 * @return the value of base
279 */
280 public boolean isBase() {
281 return base;
282 }
283
284 /**
285 * Set the value of base.
286 *
287 * @param base new value of base
288 */
289 public void setBase(boolean base) {
290 this.base = base;
291 }
292
293 /**
294 * Processes a given dependency to determine if any CPE, CVE, CWE, or CVSS scores should be suppressed. If any should be, they
295 * are removed from the dependency.
296 *
297 * @param dependency a project dependency to analyze
298 */
299 public void process(Dependency dependency) {
300 if (filePath != null && !filePath.matches(dependency.getFilePath())) {
301 return;
302 }
303 if (sha1 != null && !sha1.equalsIgnoreCase(dependency.getSha1sum())) {
304 return;
305 }
306 if (gav != null) {
307 final Iterator<Identifier> itr = dependency.getIdentifiers().iterator();
308 boolean gavFound = false;
309 while (itr.hasNext()) {
310 final Identifier i = itr.next();
311 if (identifierMatches("maven", this.gav, i)) {
312 gavFound = true;
313 break;
314 }
315 }
316 if (!gavFound) {
317 return;
318 }
319 }
320
321 if (this.hasCpe()) {
322 final Iterator<Identifier> itr = dependency.getIdentifiers().iterator();
323 while (itr.hasNext()) {
324 final Identifier i = itr.next();
325 for (PropertyType c : this.cpe) {
326 if (identifierMatches("cpe", c, i)) {
327 if (!isBase()) {
328 dependency.addSuppressedIdentifier(i);
329 }
330 itr.remove();
331 break;
332 }
333 }
334 }
335 }
336 if (hasCve() || hasCwe() || hasCvssBelow()) {
337 final Iterator<Vulnerability> itr = dependency.getVulnerabilities().iterator();
338 while (itr.hasNext()) {
339 boolean remove = false;
340 final Vulnerability v = itr.next();
341 for (String entry : this.cve) {
342 if (entry.equalsIgnoreCase(v.getName())) {
343 remove = true;
344 break;
345 }
346 }
347 if (!remove) {
348 for (String entry : this.cwe) {
349 if (v.getCwe() != null) {
350 final String toMatch = String.format("CWE-%s ", entry);
351 final String toTest = v.getCwe().substring(0, toMatch.length()).toUpperCase();
352 if (toTest.equals(toMatch)) {
353 remove = true;
354 break;
355 }
356 }
357 }
358 }
359 if (!remove) {
360 for (float cvss : this.cvssBelow) {
361 if (v.getCvssScore() < cvss) {
362 remove = true;
363 break;
364 }
365 }
366 }
367 if (remove) {
368 if (!isBase()) {
369 dependency.addSuppressedVulnerability(v);
370 }
371 itr.remove();
372 }
373 }
374 }
375 }
376
377 /**
378 * Identifies if the cpe specified by the cpe suppression rule does not specify a version.
379 *
380 * @param c a suppression rule identifier
381 * @return true if the property type does not specify a version; otherwise false
382 */
383 boolean cpeHasNoVersion(PropertyType c) {
384 return !c.isRegex() && countCharacter(c.getValue(), ':') <= 3;
385 }
386
387 /**
388 * Counts the number of occurrences of the character found within the string.
389 *
390 * @param str the string to check
391 * @param c the character to count
392 * @return the number of times the character is found in the string
393 */
394 int countCharacter(String str, char c) {
395 int count = 0;
396 int pos = str.indexOf(c) + 1;
397 while (pos > 0) {
398 count += 1;
399 pos = str.indexOf(c, pos) + 1;
400 }
401 return count;
402 }
403
404 /**
405 * Determines if the cpeEntry specified as a PropertyType matches the given Identifier.
406 *
407 * @param identifierType the type of identifier ("cpe", "maven", etc.)
408 * @param suppressionEntry a suppression rule entry
409 * @param identifier a CPE identifier to check
410 * @return true if the entry matches; otherwise false
411 */
412 boolean identifierMatches(String identifierType, PropertyType suppressionEntry, Identifier identifier) {
413 if (identifierType.equals(identifier.getType())) {
414 if (suppressionEntry.matches(identifier.getValue())) {
415 return true;
416 } else if ("cpe".equals(identifierType) && cpeHasNoVersion(suppressionEntry)) {
417 if (suppressionEntry.isCaseSensitive()) {
418 return identifier.getValue().startsWith(suppressionEntry.getValue());
419 } else {
420 final String id = identifier.getValue().toLowerCase();
421 final String check = suppressionEntry.getValue().toLowerCase();
422 return id.startsWith(check);
423 }
424 }
425 }
426 return false;
427 }
428
429 /**
430 * Standard toString implementation.
431 *
432 * @return a string representation of this object
433 */
434 @Override
435 public String toString() {
436 final StringBuilder sb = new StringBuilder(64);
437 sb.append("SuppressionRule{");
438 if (filePath != null) {
439 sb.append("filePath=").append(filePath).append(',');
440 }
441 if (sha1 != null) {
442 sb.append("sha1=").append(sha1).append(',');
443 }
444 if (gav != null) {
445 sb.append("gav=").append(gav).append(',');
446 }
447 if (cpe != null && !cpe.isEmpty()) {
448 sb.append("cpe={");
449 for (PropertyType pt : cpe) {
450 sb.append(pt).append(',');
451 }
452 sb.append('}');
453 }
454 if (cwe != null && !cwe.isEmpty()) {
455 sb.append("cwe={");
456 for (String s : cwe) {
457 sb.append(s).append(',');
458 }
459 sb.append('}');
460 }
461 if (cve != null && !cve.isEmpty()) {
462 sb.append("cve={");
463 for (String s : cve) {
464 sb.append(s).append(',');
465 }
466 sb.append('}');
467 }
468 if (cvssBelow != null && !cvssBelow.isEmpty()) {
469 sb.append("cvssBelow={");
470 for (Float s : cvssBelow) {
471 sb.append(s).append(',');
472 }
473 sb.append('}');
474 }
475 sb.append('}');
476 return sb.toString();
477 }
478 }