Coverage Report - org.owasp.dependencycheck.dependency.EvidenceCollection
 
Classes in this File Line Coverage Branch Coverage Complexity
EvidenceCollection
71%
75/105
61%
38/62
2.792
EvidenceCollection$1
100%
2/2
100%
2/2
2.792
EvidenceCollection$2
100%
2/2
100%
2/2
2.792
EvidenceCollection$3
100%
2/2
100%
2/2
2.792
EvidenceCollection$4
100%
2/2
100%
2/2
2.792
EvidenceCollection$5
100%
2/2
N/A
2.792
 
 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) 2012 Jeremy Long. All Rights Reserved.
 17  
  */
 18  
 package org.owasp.dependencycheck.dependency;
 19  
 
 20  
 import java.io.Serializable;
 21  
 import java.net.MalformedURLException;
 22  
 import java.util.HashSet;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.Set;
 26  
 import java.util.TreeSet;
 27  
 import org.apache.commons.lang.StringUtils;
 28  
 import org.owasp.dependencycheck.utils.DependencyVersion;
 29  
 import org.owasp.dependencycheck.utils.DependencyVersionUtil;
 30  
 import org.owasp.dependencycheck.utils.Filter;
 31  
 import org.owasp.dependencycheck.utils.UrlStringUtils;
 32  
 import org.slf4j.Logger;
 33  
 import org.slf4j.LoggerFactory;
 34  
 
 35  
 /**
 36  
  * Used to maintain a collection of Evidence.
 37  
  *
 38  
  * @author Jeremy Long
 39  
  */
 40  
 public class EvidenceCollection implements Serializable, Iterable<Evidence> {
 41  
 
 42  
     /**
 43  
      * The logger.
 44  
      */
 45  8
     private static final Logger LOGGER = LoggerFactory.getLogger(EvidenceCollection.class);
 46  
     /**
 47  
      * Used to iterate over highest confidence evidence contained in the collection.
 48  
      */
 49  512
     private static final Filter<Evidence> HIGHEST_CONFIDENCE = new Filter<Evidence>() {
 50  
         public boolean passes(Evidence evidence) {
 51  504
             return evidence.getConfidence() == Confidence.HIGHEST;
 52  
         }
 53  
     };
 54  
     /**
 55  
      * Used to iterate over high confidence evidence contained in the collection.
 56  
      */
 57  328
     private static final Filter<Evidence> HIGH_CONFIDENCE = new Filter<Evidence>() {
 58  
         public boolean passes(Evidence evidence) {
 59  320
             return evidence.getConfidence() == Confidence.HIGH;
 60  
         }
 61  
     };
 62  
     /**
 63  
      * Used to iterate over medium confidence evidence contained in the collection.
 64  
      */
 65  8
     private static final Filter<Evidence> MEDIUM_CONFIDENCE = new Filter<Evidence>() {
 66  
         public boolean passes(Evidence evidence) {
 67  296
             return evidence.getConfidence() == Confidence.MEDIUM;
 68  
         }
 69  
     };
 70  
     /**
 71  
      * Used to iterate over low confidence evidence contained in the collection.
 72  
      */
 73  8
     private static final Filter<Evidence> LOW_CONFIDENCE = new Filter<Evidence>() {
 74  
         public boolean passes(Evidence evidence) {
 75  296
             return evidence.getConfidence() == Confidence.LOW;
 76  
         }
 77  
     };
 78  
     /**
 79  
      * Used to iterate over evidence that has was used (aka read) from the collection.
 80  
      */
 81  8
     private static final Filter<Evidence> EVIDENCE_USED = new Filter<Evidence>() {
 82  
         public boolean passes(Evidence evidence) {
 83  8640
             return evidence.isUsed();
 84  
         }
 85  
     };
 86  
 
 87  
     /**
 88  
      * Used to iterate over evidence of the specified confidence.
 89  
      *
 90  
      * @param confidence the confidence level for the evidence to be iterated over.
 91  
      * @return Iterable<Evidence> an iterable collection of evidence
 92  
      */
 93  
     public final Iterable<Evidence> iterator(Confidence confidence) {
 94  184
         if (confidence == Confidence.HIGHEST) {
 95  56
             return EvidenceCollection.HIGHEST_CONFIDENCE.filter(this.list);
 96  128
         } else if (confidence == Confidence.HIGH) {
 97  48
             return EvidenceCollection.HIGH_CONFIDENCE.filter(this.list);
 98  80
         } else if (confidence == Confidence.MEDIUM) {
 99  40
             return EvidenceCollection.MEDIUM_CONFIDENCE.filter(this.list);
 100  
         } else {
 101  40
             return EvidenceCollection.LOW_CONFIDENCE.filter(this.list);
 102  
         }
 103  
     }
 104  
     /**
 105  
      * A collection of evidence.
 106  
      */
 107  
     private final Set<Evidence> list;
 108  
     /**
 109  
      * A collection of strings used to adjust Lucene's term weighting.
 110  
      */
 111  
     private final Set<String> weightedStrings;
 112  
 
 113  
     /**
 114  
      * Creates a new EvidenceCollection.
 115  
      */
 116  1392
     public EvidenceCollection() {
 117  1392
         list = new TreeSet<Evidence>();
 118  1392
         weightedStrings = new HashSet<String>();
 119  1392
     }
 120  
 
 121  
     /**
 122  
      * Adds evidence to the collection.
 123  
      *
 124  
      * @param e Evidence.
 125  
      */
 126  
     public void addEvidence(Evidence e) {
 127  116856
         list.add(e);
 128  116856
     }
 129  
 
 130  
     /**
 131  
      * Creates an Evidence object from the parameters and adds the resulting object to the collection.
 132  
      *
 133  
      * @param source the source of the Evidence.
 134  
      * @param name the name of the Evidence.
 135  
      * @param value the value of the Evidence.
 136  
      * @param confidence the confidence of the Evidence.
 137  
      */
 138  
     public void addEvidence(String source, String name, String value, Confidence confidence) {
 139  116800
         final Evidence e = new Evidence(source, name, value, confidence);
 140  116800
         addEvidence(e);
 141  116800
     }
 142  
 
 143  
     /**
 144  
      * Adds term to the weighting collection. The terms added here are used later to boost the score of other terms. This is a way
 145  
      * of combining evidence from multiple sources to boost the confidence of the given evidence.
 146  
      *
 147  
      * Example: The term 'Apache' is found in the manifest of a JAR and is added to the Collection. When we parse the package
 148  
      * names within the JAR file we may add these package names to the "weighted" strings collection to boost the score in the
 149  
      * Lucene query. That way when we construct the Lucene query we find the term Apache in the collection AND in the weighted
 150  
      * strings; as such, we will boost the confidence of the term Apache.
 151  
      *
 152  
      * @param str to add to the weighting collection.
 153  
      */
 154  
     public void addWeighting(String str) {
 155  120
         weightedStrings.add(str);
 156  120
     }
 157  
 
 158  
     /**
 159  
      * Returns a set of Weightings - a list of terms that are believed to be of higher confidence when also found in another
 160  
      * location.
 161  
      *
 162  
      * @return Set<String>
 163  
      */
 164  
     public Set<String> getWeighting() {
 165  104
         return weightedStrings;
 166  
     }
 167  
 
 168  
     /**
 169  
      * Returns the set of evidence.
 170  
      *
 171  
      * @return the set of evidence.
 172  
      */
 173  
     public Set<Evidence> getEvidence() {
 174  72
         return list;
 175  
     }
 176  
 
 177  
     /**
 178  
      * Returns the set of evidence from a given source.
 179  
      *
 180  
      * @param source the source of the evidence
 181  
      * @return the set of evidence.
 182  
      */
 183  
     public Set<Evidence> getEvidence(String source) {
 184  0
         if (source == null) {
 185  0
             return null;
 186  
         }
 187  0
         final Set<Evidence> ret = new HashSet<Evidence>();
 188  0
         for (Evidence e : list) {
 189  0
             if (source.equals(e.getSource())) {
 190  0
                 ret.add(e);
 191  
             }
 192  0
         }
 193  0
         return ret;
 194  
     }
 195  
 
 196  
     /**
 197  
      * Returns the set of evidence from a given source and name.
 198  
      *
 199  
      * @param source the source of the evidence
 200  
      * @param name the name of the evidence to return
 201  
      * @return the set of evidence.
 202  
      */
 203  
     public Set<Evidence> getEvidence(String source, String name) {
 204  64
         if (source == null || name == null) {
 205  0
             return null;
 206  
         }
 207  64
         final Set<Evidence> ret = new HashSet<Evidence>();
 208  64
         for (Evidence e : list) {
 209  88
             if (source.equals(e.getSource()) && name.equals(e.getName())) {
 210  40
                 ret.add(e);
 211  
             }
 212  88
         }
 213  64
         return ret;
 214  
     }
 215  
 
 216  
     /**
 217  
      * Implements the iterator interface for the Evidence Collection.
 218  
      *
 219  
      * @return an Iterator<Evidence>.
 220  
      */
 221  
     public Iterator<Evidence> iterator() {
 222  1048
         return list.iterator();
 223  
     }
 224  
 
 225  
     /**
 226  
      * Used to determine if a given string was used (aka read).
 227  
      *
 228  
      * @param text the string to search for.
 229  
      * @return whether or not the string was used.
 230  
      */
 231  
     public boolean containsUsedString(String text) {
 232  952
         if (text == null) {
 233  0
             return false;
 234  
         }
 235  952
         final String textToTest = text.toLowerCase();
 236  
 
 237  952
         for (Evidence e : EvidenceCollection.EVIDENCE_USED.filter(this)) {
 238  
             //TODO consider changing the regex to only compare alpha-numeric (i.e. strip everything else)
 239  5160
             final String value = urlCorrection(e.getValue().toLowerCase()).replaceAll("[\\s_-]", "");
 240  5160
             if (value.contains(textToTest)) {
 241  392
                 return true;
 242  
             }
 243  4768
         }
 244  560
         return false;
 245  
     }
 246  
 
 247  
     /**
 248  
      * Used to determine if a given version was used (aka read) from the EvidenceCollection.
 249  
      *
 250  
      * @param version the version to search for within the collected evidence.
 251  
      * @return whether or not the string was used.
 252  
      */
 253  
     public boolean containsUsedVersion(DependencyVersion version) {
 254  0
         if (version == null) {
 255  0
             return false;
 256  
         }
 257  
 
 258  0
         for (Evidence e : EvidenceCollection.EVIDENCE_USED.filter(this)) {
 259  0
             final DependencyVersion value = DependencyVersionUtil.parseVersion(e.getValue());
 260  0
             if (value != null && value.matchesAtLeastThreeLevels(version)) {
 261  0
                 return true;
 262  
             }
 263  0
         }
 264  0
         return false;
 265  
     }
 266  
 
 267  
     /**
 268  
      * Returns whether or not the collection contains evidence of a specified Confidence.
 269  
      *
 270  
      * @param confidence A Confidence value.
 271  
      * @return boolean.
 272  
      */
 273  
     public boolean contains(Confidence confidence) {
 274  112
         for (Evidence e : list) {
 275  488
             if (e.getConfidence().equals(confidence)) {
 276  88
                 return true;
 277  
             }
 278  400
         }
 279  24
         return false;
 280  
     }
 281  
 
 282  
     /**
 283  
      * Merges multiple EvidenceCollections together, only merging evidence that was used, into a new EvidenceCollection.
 284  
      *
 285  
      * @param ec One or more EvidenceCollections.
 286  
      * @return a new EvidenceCollection containing the used evidence.
 287  
      */
 288  
     public static EvidenceCollection mergeUsed(EvidenceCollection... ec) {
 289  8
         final EvidenceCollection ret = new EvidenceCollection();
 290  32
         for (EvidenceCollection col : ec) {
 291  24
             for (Evidence e : col.list) {
 292  16
                 if (e.isUsed()) {
 293  8
                     ret.addEvidence(e);
 294  
                 }
 295  16
             }
 296  
         }
 297  8
         return ret;
 298  
     }
 299  
 
 300  
     /**
 301  
      * Merges multiple EvidenceCollections together.
 302  
      *
 303  
      * @param ec One or more EvidenceCollections.
 304  
      * @return a new EvidenceCollection.
 305  
      */
 306  
     public static EvidenceCollection merge(EvidenceCollection... ec) {
 307  88
         final EvidenceCollection ret = new EvidenceCollection();
 308  352
         for (EvidenceCollection col : ec) {
 309  264
             ret.list.addAll(col.list);
 310  264
             ret.weightedStrings.addAll(col.weightedStrings);
 311  
         }
 312  88
         return ret;
 313  
     }
 314  
 
 315  
     /**
 316  
      * Merges multiple EvidenceCollections together; flattening all of the evidence items by removing the confidence.
 317  
      *
 318  
      * @param ec One or more EvidenceCollections
 319  
      * @return new set of evidence resulting from merging the evidence in the collections
 320  
      */
 321  
     public static Set<Evidence> mergeForDisplay(EvidenceCollection... ec) {
 322  0
         final Set<Evidence> ret = new TreeSet<Evidence>();
 323  0
         for (EvidenceCollection col : ec) {
 324  0
             for (Evidence e : col) {
 325  
                 //if (e.isUsed()) {
 326  0
                 final Evidence newEvidence = new Evidence(e.getSource(), e.getName(), e.getValue(), null);
 327  0
                 newEvidence.setUsed(true);
 328  0
                 ret.add(newEvidence);
 329  
                 //}
 330  0
             }
 331  
         }
 332  0
         return ret;
 333  
     }
 334  
 
 335  
     /**
 336  
      * Returns a string of evidence 'values'.
 337  
      *
 338  
      * @return a string containing the evidence.
 339  
      */
 340  
     @Override
 341  
     public String toString() {
 342  264
         final StringBuilder sb = new StringBuilder();
 343  264
         for (Evidence e : this.list) {
 344  512
             sb.append(e.getValue()).append(' ');
 345  512
         }
 346  264
         return sb.toString();
 347  
     }
 348  
 
 349  
     /**
 350  
      * Returns the number of elements in the EvidenceCollection.
 351  
      *
 352  
      * @return the number of elements in the collection.
 353  
      */
 354  
     public int size() {
 355  96
         return list.size();
 356  
     }
 357  
 
 358  
     /**
 359  
      * <p>
 360  
      * Takes a string that may contain a fully qualified domain and it will return the string having removed the query string, the
 361  
      * protocol, the sub-domain of 'www', and the file extension of the path.</p>
 362  
      * <p>
 363  
      * This is useful for checking if the evidence contains a specific string. The presence of the protocol, file extension, etc.
 364  
      * may produce false positives.
 365  
      *
 366  
      * <p>
 367  
      * Example, given the following input:</p>
 368  
      * <code>'Please visit https://www.somedomain.com/path1/path2/file.php?id=439'</code>
 369  
      * <p>
 370  
      * The function would return:</p>
 371  
      * <code>'Please visit somedomain path1 path2 file'</code>
 372  
      *
 373  
      * @param value the value that may contain a url
 374  
      * @return the modified string
 375  
      */
 376  
     private String urlCorrection(String value) {
 377  5160
         if (value == null || !UrlStringUtils.containsUrl(value)) {
 378  5008
             return value;
 379  
         }
 380  152
         final StringBuilder sb = new StringBuilder(value.length());
 381  152
         final String[] parts = value.split("\\s");
 382  304
         for (String part : parts) {
 383  152
             if (UrlStringUtils.isUrl(part)) {
 384  
                 try {
 385  152
                     final List<String> data = UrlStringUtils.extractImportantUrlData(part);
 386  152
                     sb.append(' ').append(StringUtils.join(data, ' '));
 387  0
                 } catch (MalformedURLException ex) {
 388  0
                     LOGGER.debug("error parsing {}", part, ex);
 389  0
                     sb.append(' ').append(part);
 390  152
                 }
 391  
             } else {
 392  0
                 sb.append(' ').append(part);
 393  
             }
 394  
         }
 395  152
         return sb.toString().trim();
 396  
     }
 397  
 }