diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java index 2cf35171d..701897b7c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java @@ -138,6 +138,14 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal mergeDependencies(nextDependency, dependency, dependenciesToRemove); break; //since we merged into the next dependency - skip forward to the next in mainIterator } + } else if ( isSameRubyGem(dependency, nextDependency) ) { + Dependency main = getMainGemspecDependency(dependency, nextDependency); + if (main == dependency) { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + } else { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + break; //since we merged into the next dependency - skip forward to the next in mainIterator + } } } } @@ -302,6 +310,40 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal } return false; } + + private boolean isSameRubyGem(Dependency dependency1, Dependency dependency2) { + if (dependency1 == null || dependency2 == null || + !dependency1.getFileName().endsWith(".gemspec") || + !dependency2.getFileName().endsWith(".gemspec") || + dependency1.getPackagePath() == null || + dependency2.getPackagePath() == null) { + return false; + } + if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) + return true; + + return false; + } + + /** + * A gem install may have zero or more *.gemspec files, all of which have the same packagePath and should be grouped. + * If one of these gemspec is from /specifications/*.gemspec, which is a stub with fully resolved gem meta-data + * created by Ruby bundler, this dependency should be the main one. Otherwise, use dependency2 as main. + * + * This method returns null if any dependency is not from *.gemspec, or the two do not have the same packagePath. + * In this case, they should not be grouped. + */ + private Dependency getMainGemspecDependency(Dependency dependency1, Dependency dependency2) { + if (isSameRubyGem(dependency1, dependency2)) { + final File lFile = dependency1.getActualFile(); + File left = lFile.getParentFile(); + if (left != null && left.getName().equalsIgnoreCase("specifications")) { + return dependency1; + } + return dependency2; + } + return null; + } /** * This is likely a very broken attempt at determining if the 'left' dependency is the 'core' library in comparison to the