Coverage Report - org.owasp.dependencycheck.analyzer.FalsePositiveAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
FalsePositiveAnalyzer
47%
106/224
25%
58/230
10.385
 
 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.analyzer;
 19  
 
 20  
 import java.io.FileFilter;
 21  
 import java.io.UnsupportedEncodingException;
 22  
 import java.net.URLEncoder;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Collections;
 25  
 import java.util.Iterator;
 26  
 import java.util.List;
 27  
 import java.util.ListIterator;
 28  
 import java.util.Set;
 29  
 import java.util.regex.Matcher;
 30  
 import java.util.regex.Pattern;
 31  
 import org.owasp.dependencycheck.Engine;
 32  
 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
 33  
 import org.owasp.dependencycheck.dependency.Dependency;
 34  
 import org.owasp.dependencycheck.dependency.Identifier;
 35  
 import org.owasp.dependencycheck.dependency.VulnerableSoftware;
 36  
 import org.owasp.dependencycheck.utils.FileFilterBuilder;
 37  
 import org.slf4j.Logger;
 38  
 import org.slf4j.LoggerFactory;
 39  
 
 40  
 /**
 41  
  * This analyzer attempts to remove some well known false positives - specifically regarding the java runtime.
 42  
  *
 43  
  * @author Jeremy Long
 44  
  */
 45  7
 public class FalsePositiveAnalyzer extends AbstractAnalyzer {
 46  
 
 47  
     /**
 48  
      * The Logger.
 49  
      */
 50  1
     private static final Logger LOGGER = LoggerFactory.getLogger(FalsePositiveAnalyzer.class);
 51  
 
 52  
     /**
 53  
      * The file filter used to find DLL and EXE.
 54  
      */
 55  1
     private static final FileFilter DLL_EXE_FILTER = FileFilterBuilder.newInstance().addExtensions("dll", "exe").build();
 56  
 
 57  
     //<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
 58  
     /**
 59  
      * The name of the analyzer.
 60  
      */
 61  
     private static final String ANALYZER_NAME = "False Positive Analyzer";
 62  
     /**
 63  
      * The phase that this analyzer is intended to run in.
 64  
      */
 65  1
     private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_IDENTIFIER_ANALYSIS;
 66  
 
 67  
     /**
 68  
      * Returns the name of the analyzer.
 69  
      *
 70  
      * @return the name of the analyzer.
 71  
      */
 72  
     @Override
 73  
     public String getName() {
 74  5
         return ANALYZER_NAME;
 75  
     }
 76  
 
 77  
     /**
 78  
      * Returns the phase that the analyzer is intended to run in.
 79  
      *
 80  
      * @return the phase that the analyzer is intended to run in.
 81  
      */
 82  
     @Override
 83  
     public AnalysisPhase getAnalysisPhase() {
 84  4
         return ANALYSIS_PHASE;
 85  
     }
 86  
     //</editor-fold>
 87  
 
 88  
     /**
 89  
      * Analyzes the dependencies and removes bad/incorrect CPE associations based on various heuristics.
 90  
      *
 91  
      * @param dependency the dependency to analyze.
 92  
      * @param engine the engine that is scanning the dependencies
 93  
      * @throws AnalysisException is thrown if there is an error reading the JAR file.
 94  
      */
 95  
     @Override
 96  
     public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
 97  3
         removeJreEntries(dependency);
 98  3
         removeBadMatches(dependency);
 99  3
         removeBadSpringMatches(dependency);
 100  3
         removeWrongVersionMatches(dependency);
 101  3
         removeSpuriousCPE(dependency);
 102  3
         removeDuplicativeEntriesFromJar(dependency, engine);
 103  3
         addFalseNegativeCPEs(dependency);
 104  3
     }
 105  
 
 106  
     /**
 107  
      * Removes inaccurate matches on springframework CPEs.
 108  
      *
 109  
      * @param dependency the dependency to test for and remove known inaccurate CPE matches
 110  
      */
 111  
     private void removeBadSpringMatches(Dependency dependency) {
 112  3
         String mustContain = null;
 113  3
         for (Identifier i : dependency.getIdentifiers()) {
 114  3
             if ("maven".contains(i.getType())) {
 115  0
                 if (i.getValue() != null && i.getValue().startsWith("org.springframework.")) {
 116  0
                     final int endPoint = i.getValue().indexOf(':', 19);
 117  0
                     if (endPoint >= 0) {
 118  0
                         mustContain = i.getValue().substring(19, endPoint).toLowerCase();
 119  0
                         break;
 120  
                     }
 121  
                 }
 122  
             }
 123  3
         }
 124  3
         if (mustContain != null) {
 125  0
             final Iterator<Identifier> itr = dependency.getIdentifiers().iterator();
 126  0
             while (itr.hasNext()) {
 127  0
                 final Identifier i = itr.next();
 128  0
                 if ("cpe".contains(i.getType())
 129  0
                         && i.getValue() != null
 130  0
                         && i.getValue().startsWith("cpe:/a:springsource:")
 131  0
                         && !i.getValue().toLowerCase().contains(mustContain)) {
 132  0
                     itr.remove();
 133  
                     //dependency.getIdentifiers().remove(i);
 134  
                 }
 135  0
             }
 136  
         }
 137  3
     }
 138  
 
 139  
     /**
 140  
      * <p>
 141  
      * Intended to remove spurious CPE entries. By spurious we mean duplicate, less specific CPE entries.</p>
 142  
      * <p>
 143  
      * Example:</p>
 144  
      * <code>
 145  
      * cpe:/a:some-vendor:some-product
 146  
      * cpe:/a:some-vendor:some-product:1.5
 147  
      * cpe:/a:some-vendor:some-product:1.5.2
 148  
      * </code>
 149  
      * <p>
 150  
      * Should be trimmed to:</p>
 151  
      * <code>
 152  
      * cpe:/a:some-vendor:some-product:1.5.2
 153  
      * </code>
 154  
      *
 155  
      * @param dependency the dependency being analyzed
 156  
      */
 157  
     @SuppressWarnings("null")
 158  
     private void removeSpuriousCPE(Dependency dependency) {
 159  3
         final List<Identifier> ids = new ArrayList<Identifier>(dependency.getIdentifiers());
 160  3
         Collections.sort(ids);
 161  3
         final ListIterator<Identifier> mainItr = ids.listIterator();
 162  6
         while (mainItr.hasNext()) {
 163  3
             final Identifier currentId = mainItr.next();
 164  3
             final VulnerableSoftware currentCpe = parseCpe(currentId.getType(), currentId.getValue());
 165  3
             if (currentCpe == null) {
 166  0
                 continue;
 167  
             }
 168  3
             final ListIterator<Identifier> subItr = ids.listIterator(mainItr.nextIndex());
 169  6
             while (subItr.hasNext()) {
 170  3
                 final Identifier nextId = subItr.next();
 171  3
                 final VulnerableSoftware nextCpe = parseCpe(nextId.getType(), nextId.getValue());
 172  3
                 if (nextCpe == null) {
 173  0
                     continue;
 174  
                 }
 175  
                 //TODO fix the version problem below
 176  3
                 if (currentCpe.getVendor().equals(nextCpe.getVendor())) {
 177  0
                     if (currentCpe.getProduct().equals(nextCpe.getProduct())) {
 178  
                         // see if one is contained in the other.. remove the contained one from dependency.getIdentifier
 179  0
                         final String currentVersion = currentCpe.getVersion();
 180  0
                         final String nextVersion = nextCpe.getVersion();
 181  0
                         if (currentVersion == null && nextVersion == null) {
 182  
                             //how did we get here?
 183  0
                             LOGGER.debug("currentVersion and nextVersion are both null?");
 184  0
                         } else if (currentVersion == null && nextVersion != null) {
 185  0
                             dependency.getIdentifiers().remove(currentId);
 186  0
                         } else if (nextVersion == null && currentVersion != null) {
 187  0
                             dependency.getIdentifiers().remove(nextId);
 188  0
                         } else if (currentVersion.length() < nextVersion.length()) {
 189  0
                             if (nextVersion.startsWith(currentVersion) || "-".equals(currentVersion)) {
 190  0
                                 dependency.getIdentifiers().remove(currentId);
 191  
                             }
 192  
                         } else {
 193  0
                             if (currentVersion.startsWith(nextVersion) || "-".equals(nextVersion)) {
 194  0
                                 dependency.getIdentifiers().remove(nextId);
 195  
                             }
 196  
                         }
 197  
                     }
 198  
                 }
 199  3
             }
 200  3
         }
 201  3
     }
 202  
     /**
 203  
      * Regex to identify core java libraries and a few other commonly misidentified ones.
 204  
      */
 205  1
     public static final Pattern CORE_JAVA = Pattern.compile("^cpe:/a:(sun|oracle|ibm):(j2[ems]e|"
 206  
             + "java(_platform_micro_edition|_runtime_environment|_se|virtual_machine|se_development_kit|fx)?|"
 207  
             + "jdk|jre|jsse)($|:.*)");
 208  
 
 209  
     /**
 210  
      * Regex to identify core jsf libraries.
 211  
      */
 212  1
     public static final Pattern CORE_JAVA_JSF = Pattern.compile("^cpe:/a:(sun|oracle|ibm):jsf($|:.*)");
 213  
     /**
 214  
      * Regex to identify core java library files. This is currently incomplete.
 215  
      */
 216  1
     public static final Pattern CORE_FILES = Pattern.compile("(^|/)((alt[-])?rt|jsse|jfxrt|jfr|jce|javaws|deploy|charsets)\\.jar$");
 217  
     /**
 218  
      * Regex to identify core jsf java library files. This is currently incomplete.
 219  
      */
 220  1
     public static final Pattern CORE_JSF_FILES = Pattern.compile("(^|/)jsf[-][^/]*\\.jar$");
 221  
 
 222  
     /**
 223  
      * Removes any CPE entries for the JDK/JRE unless the filename ends with rt.jar
 224  
      *
 225  
      * @param dependency the dependency to remove JRE CPEs from
 226  
      */
 227  
     private void removeJreEntries(Dependency dependency) {
 228  3
         final Set<Identifier> identifiers = dependency.getIdentifiers();
 229  3
         final Iterator<Identifier> itr = identifiers.iterator();
 230  7
         while (itr.hasNext()) {
 231  4
             final Identifier i = itr.next();
 232  4
             final Matcher coreCPE = CORE_JAVA.matcher(i.getValue());
 233  4
             final Matcher coreFiles = CORE_FILES.matcher(dependency.getFileName());
 234  4
             if (coreCPE.matches() && !coreFiles.matches()) {
 235  0
                 itr.remove();
 236  
             }
 237  4
             final Matcher coreJsfCPE = CORE_JAVA_JSF.matcher(i.getValue());
 238  4
             final Matcher coreJsfFiles = CORE_JSF_FILES.matcher(dependency.getFileName());
 239  4
             if (coreJsfCPE.matches() && !coreJsfFiles.matches()) {
 240  0
                 itr.remove();
 241  
             }
 242  4
         }
 243  3
     }
 244  
 
 245  
     /**
 246  
      * Parses a CPE string into an IndexEntry.
 247  
      *
 248  
      * @param type the type of identifier
 249  
      * @param value the cpe identifier to parse
 250  
      * @return an VulnerableSoftware object constructed from the identifier
 251  
      */
 252  
     private VulnerableSoftware parseCpe(String type, String value) {
 253  6
         if (!"cpe".equals(type)) {
 254  0
             return null;
 255  
         }
 256  6
         final VulnerableSoftware cpe = new VulnerableSoftware();
 257  
         try {
 258  6
             cpe.parseName(value);
 259  0
         } catch (UnsupportedEncodingException ex) {
 260  0
             LOGGER.trace("", ex);
 261  0
             return null;
 262  6
         }
 263  6
         return cpe;
 264  
     }
 265  
 
 266  
     /**
 267  
      * Removes bad CPE matches for a dependency. Unfortunately, right now these are hard-coded patches for specific problems
 268  
      * identified when testing this on a LARGE volume of jar files.
 269  
      *
 270  
      * @param dependency the dependency to analyze
 271  
      */
 272  
     private void removeBadMatches(Dependency dependency) {
 273  3
         final Set<Identifier> identifiers = dependency.getIdentifiers();
 274  3
         final Iterator<Identifier> itr = identifiers.iterator();
 275  
 
 276  
         /* TODO - can we utilize the pom's groupid and artifactId to filter??? most of
 277  
          * these are due to low quality data.  Other idea would be to say any CPE
 278  
          * found based on LOW confidence evidence should have a different CPE type? (this
 279  
          * might be a better solution then just removing the URL for "best-guess" matches).
 280  
          */
 281  
         //Set<Evidence> groupId = dependency.getVendorEvidence().getEvidence("pom", "groupid");
 282  
         //Set<Evidence> artifactId = dependency.getVendorEvidence().getEvidence("pom", "artifactid");
 283  7
         while (itr.hasNext()) {
 284  4
             final Identifier i = itr.next();
 285  
             //TODO move this startsWith expression to the base suppression file
 286  4
             if ("cpe".equals(i.getType())) {
 287  4
                 if ((i.getValue().matches(".*c\\+\\+.*")
 288  4
                         || i.getValue().startsWith("cpe:/a:file:file")
 289  3
                         || i.getValue().startsWith("cpe:/a:mozilla:mozilla")
 290  3
                         || i.getValue().startsWith("cpe:/a:cvs:cvs")
 291  3
                         || i.getValue().startsWith("cpe:/a:ftp:ftp")
 292  3
                         || i.getValue().startsWith("cpe:/a:tcp:tcp")
 293  3
                         || i.getValue().startsWith("cpe:/a:ssh:ssh")
 294  3
                         || i.getValue().startsWith("cpe:/a:lookup:lookup"))
 295  1
                         && (dependency.getFileName().toLowerCase().endsWith(".jar")
 296  1
                         || dependency.getFileName().toLowerCase().endsWith("pom.xml")
 297  0
                         || dependency.getFileName().toLowerCase().endsWith(".dll")
 298  0
                         || dependency.getFileName().toLowerCase().endsWith(".exe")
 299  0
                         || dependency.getFileName().toLowerCase().endsWith(".nuspec")
 300  0
                         || dependency.getFileName().toLowerCase().endsWith(".zip")
 301  0
                         || dependency.getFileName().toLowerCase().endsWith(".sar")
 302  0
                         || dependency.getFileName().toLowerCase().endsWith(".apk")
 303  0
                         || dependency.getFileName().toLowerCase().endsWith(".tar")
 304  0
                         || dependency.getFileName().toLowerCase().endsWith(".gz")
 305  0
                         || dependency.getFileName().toLowerCase().endsWith(".tgz")
 306  0
                         || dependency.getFileName().toLowerCase().endsWith(".ear")
 307  0
                         || dependency.getFileName().toLowerCase().endsWith(".war"))) {
 308  1
                     itr.remove();
 309  3
                 } else if ((i.getValue().startsWith("cpe:/a:jquery:jquery")
 310  3
                         || i.getValue().startsWith("cpe:/a:prototypejs:prototype")
 311  3
                         || i.getValue().startsWith("cpe:/a:yahoo:yui"))
 312  0
                         && (dependency.getFileName().toLowerCase().endsWith(".jar")
 313  0
                         || dependency.getFileName().toLowerCase().endsWith("pom.xml")
 314  0
                         || dependency.getFileName().toLowerCase().endsWith(".dll")
 315  0
                         || dependency.getFileName().toLowerCase().endsWith(".exe"))) {
 316  0
                     itr.remove();
 317  3
                 } else if ((i.getValue().startsWith("cpe:/a:microsoft:excel")
 318  3
                         || i.getValue().startsWith("cpe:/a:microsoft:word")
 319  3
                         || i.getValue().startsWith("cpe:/a:microsoft:visio")
 320  3
                         || i.getValue().startsWith("cpe:/a:microsoft:powerpoint")
 321  3
                         || i.getValue().startsWith("cpe:/a:microsoft:office")
 322  3
                         || i.getValue().startsWith("cpe:/a:core_ftp:core_ftp"))
 323  0
                         && (dependency.getFileName().toLowerCase().endsWith(".jar")
 324  0
                         || dependency.getFileName().toLowerCase().endsWith(".ear")
 325  0
                         || dependency.getFileName().toLowerCase().endsWith(".war")
 326  0
                         || dependency.getFileName().toLowerCase().endsWith("pom.xml"))) {
 327  0
                     itr.remove();
 328  3
                 } else if (i.getValue().startsWith("cpe:/a:apache:maven")
 329  0
                         && !dependency.getFileName().toLowerCase().matches("maven-core-[\\d\\.]+\\.jar")) {
 330  0
                     itr.remove();
 331  3
                 } else if (i.getValue().startsWith("cpe:/a:m-core:m-core")
 332  0
                         && !dependency.getEvidenceUsed().containsUsedString("m-core")) {
 333  0
                     itr.remove();
 334  3
                 } else if (i.getValue().startsWith("cpe:/a:jboss:jboss")
 335  0
                         && !dependency.getFileName().toLowerCase().matches("jboss-?[\\d\\.-]+(GA)?\\.jar")) {
 336  0
                     itr.remove();
 337  
                 }
 338  
             }
 339  4
         }
 340  3
     }
 341  
 
 342  
     /**
 343  
      * Removes CPE matches for the wrong version of a dependency. Currently, this only covers Axis 1 & 2.
 344  
      *
 345  
      * @param dependency the dependency to analyze
 346  
      */
 347  
     private void removeWrongVersionMatches(Dependency dependency) {
 348  3
         final Set<Identifier> identifiers = dependency.getIdentifiers();
 349  3
         final Iterator<Identifier> itr = identifiers.iterator();
 350  
 
 351  3
         final String fileName = dependency.getFileName();
 352  3
         if (fileName != null && fileName.contains("axis2")) {
 353  0
             while (itr.hasNext()) {
 354  0
                 final Identifier i = itr.next();
 355  0
                 if ("cpe".equals(i.getType())) {
 356  0
                     final String cpe = i.getValue();
 357  0
                     if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis:") || "cpe:/a:apache:axis".equals(cpe))) {
 358  0
                         itr.remove();
 359  
                     }
 360  
                 }
 361  0
             }
 362  3
         } else if (fileName != null && fileName.contains("axis")) {
 363  0
             while (itr.hasNext()) {
 364  0
                 final Identifier i = itr.next();
 365  0
                 if ("cpe".equals(i.getType())) {
 366  0
                     final String cpe = i.getValue();
 367  0
                     if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis2:") || "cpe:/a:apache:axis2".equals(cpe))) {
 368  0
                         itr.remove();
 369  
                     }
 370  
                 }
 371  0
             }
 372  
         }
 373  3
     }
 374  
 
 375  
     /**
 376  
      * There are some known CPE entries, specifically regarding sun and oracle products due to the acquisition and changes in
 377  
      * product names, that based on given evidence we can add the related CPE entries to ensure a complete list of CVE entries.
 378  
      *
 379  
      * @param dependency the dependency being analyzed
 380  
      */
 381  
     private void addFalseNegativeCPEs(Dependency dependency) {
 382  
         //TODO move this to the hint analyzer
 383  3
         for (final Identifier identifier : dependency.getIdentifiers()) {
 384  3
             if ("cpe".equals(identifier.getType()) && identifier.getValue() != null
 385  3
                     && (identifier.getValue().startsWith("cpe:/a:oracle:opensso:")
 386  3
                     || identifier.getValue().startsWith("cpe:/a:oracle:opensso_enterprise:")
 387  3
                     || identifier.getValue().startsWith("cpe:/a:sun:opensso_enterprise:")
 388  3
                     || identifier.getValue().startsWith("cpe:/a:sun:opensso:"))) {
 389  0
                 final String newCpe = String.format("cpe:/a:sun:opensso_enterprise:%s", identifier.getValue().substring(22));
 390  0
                 final String newCpe2 = String.format("cpe:/a:oracle:opensso_enterprise:%s", identifier.getValue().substring(22));
 391  0
                 final String newCpe3 = String.format("cpe:/a:sun:opensso:%s", identifier.getValue().substring(22));
 392  0
                 final String newCpe4 = String.format("cpe:/a:oracle:opensso:%s", identifier.getValue().substring(22));
 393  
                 try {
 394  0
                     dependency.addIdentifier("cpe",
 395  
                             newCpe,
 396  0
                             String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe, "UTF-8")));
 397  0
                     dependency.addIdentifier("cpe",
 398  
                             newCpe2,
 399  0
                             String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe2, "UTF-8")));
 400  0
                     dependency.addIdentifier("cpe",
 401  
                             newCpe3,
 402  0
                             String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe3, "UTF-8")));
 403  0
                     dependency.addIdentifier("cpe",
 404  
                             newCpe4,
 405  0
                             String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe4, "UTF-8")));
 406  0
                 } catch (UnsupportedEncodingException ex) {
 407  0
                     LOGGER.debug("", ex);
 408  0
                 }
 409  
             }
 410  3
         }
 411  3
     }
 412  
 
 413  
     /**
 414  
      * Removes duplicate entries identified that are contained within JAR files. These occasionally crop up due to POM entries or
 415  
      * other types of files (such as DLLs and EXEs) being contained within the JAR.
 416  
      *
 417  
      * @param dependency the dependency that might be a duplicate
 418  
      * @param engine the engine used to scan all dependencies
 419  
      */
 420  
     private void removeDuplicativeEntriesFromJar(Dependency dependency, Engine engine) {
 421  3
         if (dependency.getFileName().toLowerCase().endsWith("pom.xml")
 422  2
                 || DLL_EXE_FILTER.accept(dependency.getActualFile())) {
 423  1
             String parentPath = dependency.getFilePath().toLowerCase();
 424  1
             if (parentPath.contains(".jar")) {
 425  0
                 parentPath = parentPath.substring(0, parentPath.indexOf(".jar") + 4);
 426  0
                 final Dependency parent = findDependency(parentPath, engine.getDependencies());
 427  0
                 if (parent != null) {
 428  0
                     boolean remove = false;
 429  0
                     for (Identifier i : dependency.getIdentifiers()) {
 430  0
                         if ("cpe".equals(i.getType())) {
 431  0
                             final String trimmedCPE = trimCpeToVendor(i.getValue());
 432  0
                             for (Identifier parentId : parent.getIdentifiers()) {
 433  0
                                 if ("cpe".equals(parentId.getType()) && parentId.getValue().startsWith(trimmedCPE)) {
 434  0
                                     remove |= true;
 435  
                                 }
 436  0
                             }
 437  
                         }
 438  0
                         if (!remove) { //we can escape early
 439  0
                             return;
 440  
                         }
 441  0
                     }
 442  0
                     if (remove) {
 443  0
                         engine.getDependencies().remove(dependency);
 444  
                     }
 445  
                 }
 446  
             }
 447  
 
 448  
         }
 449  3
     }
 450  
 
 451  
     /**
 452  
      * Retrieves a given dependency, based on a given path, from a list of dependencies.
 453  
      *
 454  
      * @param dependencyPath the path of the dependency to return
 455  
      * @param dependencies the collection of dependencies to search
 456  
      * @return the dependency object for the given path, otherwise null
 457  
      */
 458  
     private Dependency findDependency(String dependencyPath, List<Dependency> dependencies) {
 459  0
         for (Dependency d : dependencies) {
 460  0
             if (d.getFilePath().equalsIgnoreCase(dependencyPath)) {
 461  0
                 return d;
 462  
             }
 463  0
         }
 464  0
         return null;
 465  
     }
 466  
 
 467  
     /**
 468  
      * Takes a full CPE and returns the CPE trimmed to include only vendor and product.
 469  
      *
 470  
      * @param value the CPE value to trim
 471  
      * @return a CPE value that only includes the vendor and product
 472  
      */
 473  
     private String trimCpeToVendor(String value) {
 474  
         //cpe:/a:jruby:jruby:1.0.8
 475  0
         final int pos1 = value.indexOf(':', 7); //right of vendor
 476  0
         final int pos2 = value.indexOf(':', pos1 + 1); //right of product
 477  0
         if (pos2 < 0) {
 478  0
             return value;
 479  
         } else {
 480  0
             return value.substring(0, pos2);
 481  
         }
 482  
     }
 483  
 }