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.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 }