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