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