All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.owasp.dependencycheck.analyzer.DependencyMergingAnalyzer Maven / Gradle / Ivy

/*
 * This file is part of dependency-check-core.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
 */
package org.owasp.dependencycheck.analyzer;

import java.io.File;
import java.util.Set;
import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.EvidenceType;
import org.owasp.dependencycheck.utils.FileUtils;
import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

* This analyzer will merge dependencies, created from different source, into a * single dependency.

* * @author Jeremy Long */ public class DependencyMergingAnalyzer extends AbstractDependencyComparingAnalyzer { /** * The Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(DependencyMergingAnalyzer.class); /** * The name of the analyzer. */ private static final String ANALYZER_NAME = "Dependency Merging Analyzer"; /** * The phase that this analyzer is intended to run in. */ private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_INFORMATION_COLLECTION1; /** * Used for synchronization when merging related dependencies. */ private static final Object DEPENDENCY_LOCK = new Object(); /** * Returns the name of the analyzer. * * @return the name of the analyzer. */ @Override public String getName() { return ANALYZER_NAME; } /** * Returns the phase that the analyzer is intended to run in. * * @return the phase that the analyzer is intended to run in. */ @Override public AnalysisPhase getAnalysisPhase() { return ANALYSIS_PHASE; } /** *

* Returns the setting key to determine if the analyzer is enabled.

* * @return the key for the analyzer's enabled property */ @Override protected String getAnalyzerEnabledSettingKey() { return Settings.KEYS.ANALYZER_DEPENDENCY_MERGING_ENABLED; } /** * Evaluates the dependencies * * @param dependency a dependency to compare * @param nextDependency a dependency to compare * @param dependenciesToRemove a set of dependencies that will be removed * @return true if a dependency is removed; otherwise false */ @Override @SuppressWarnings("ReferenceEquality") protected boolean evaluateDependencies(final Dependency dependency, final Dependency nextDependency, final Set dependenciesToRemove) { Dependency main; //CSOFF: InnerAssignment if ((main = getMainGemspecDependency(dependency, nextDependency)) != null) { if (main == dependency) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { mergeDependencies(nextDependency, dependency, dependenciesToRemove); return true; //since we merged into the next dependency - skip forward to the next in mainIterator } } else if ((main = getMainSwiftDependency(dependency, nextDependency)) != null) { if (main == dependency) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { mergeDependencies(nextDependency, dependency, dependenciesToRemove); return true; //since we merged into the next dependency - skip forward to the next in mainIterator } } else if ((main = getMainAndroidDependency(dependency, nextDependency)) != null) { if (main == dependency) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { mergeDependencies(nextDependency, dependency, dependenciesToRemove); return true; //since we merged into the next dependency - skip forward to the next in mainIterator } } else if ((main = getMainDotnetDependency(dependency, nextDependency)) != null) { if (main == dependency) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { mergeDependencies(nextDependency, dependency, dependenciesToRemove); return true; //since we merged into the next dependency - skip forward to the next in mainIterator } } else if ((main = getMainVirtualDependency(dependency, nextDependency)) != null) { if (main == dependency) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { mergeDependencies(nextDependency, dependency, dependenciesToRemove); return true; //since we merged into the next dependency - skip forward to the next in mainIterator } } //CSON: InnerAssignment return false; } /** * Adds the relatedDependency to the dependency's related dependencies. * * @param dependency the main dependency * @param relatedDependency a collection of dependencies to be removed from * the main analysis loop, this is the source of dependencies to remove * @param dependenciesToRemove a collection of dependencies that will be * removed from the main analysis loop, this function adds to this * collection */ public static void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set dependenciesToRemove) { synchronized (DEPENDENCY_LOCK) { LOGGER.debug("Merging '{}' into '{}'", relatedDependency.getFilePath(), dependency.getFilePath()); dependency.addRelatedDependency(relatedDependency); relatedDependency.getEvidence(EvidenceType.VENDOR).forEach((e) -> dependency.addEvidence(EvidenceType.VENDOR, e)); relatedDependency.getEvidence(EvidenceType.PRODUCT).forEach((e) -> dependency.addEvidence(EvidenceType.PRODUCT, e)); relatedDependency.getEvidence(EvidenceType.VERSION).forEach((e) -> dependency.addEvidence(EvidenceType.VERSION, e)); relatedDependency.getRelatedDependencies() .forEach(dependency::addRelatedDependency); relatedDependency.clearRelatedDependencies(); dependency.addAllProjectReferences(relatedDependency.getProjectReferences()); if (dependenciesToRemove != null) { dependenciesToRemove.add(relatedDependency); } } } /** * Bundling Ruby gems that are identified from different .gemspec files but * denote the same package path. This happens when Ruby bundler installs an * application's dependencies by running "bundle install". * * @param dependency1 dependency to compare * @param dependency2 dependency to compare * @return true if the the dependencies being analyzed appear to be the * same; otherwise false */ protected 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; } return dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath()); } /** * Ruby gems installed by "bundle install" can have zero or more *.gemspec * files, all of which have the same packagePath and should be grouped. If * one of these gemspec is from <parent>/specifications/*.gemspec, * because it 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. * * @param dependency1 dependency to compare * @param dependency2 dependency to compare * @return the main dependency; or null if a gemspec is not included in the * analysis */ protected Dependency getMainGemspecDependency(Dependency dependency1, Dependency dependency2) { if (dependency1 != null && dependency2 != null && Ecosystem.RUBY.equals(dependency1.getEcosystem()) && Ecosystem.RUBY.equals(dependency2.getEcosystem()) && isSameRubyGem(dependency1, dependency2)) { final File lFile = dependency1.getActualFile(); final File left = lFile.getParentFile(); if (left != null && left.getName().equalsIgnoreCase("specifications")) { return dependency1; } return dependency2; } return null; } /** * Bundling same swift dependencies with the same packagePath but identified * by different file type analyzers. * * @param dependency1 dependency to test * @param dependency2 dependency to test * @return true if the dependencies appear to be the same; * otherwise false */ protected boolean isSameSwiftPackage(Dependency dependency1, Dependency dependency2) { if (dependency1 == null || dependency2 == null || (!dependency1.getFileName().endsWith(".podspec") && !dependency1.getFileName().equals("Package.swift")) || (!dependency2.getFileName().endsWith(".podspec") && !dependency2.getFileName().equals("Package.swift")) || dependency1.getPackagePath() == null || dependency2.getPackagePath() == null) { return false; } return dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath()); } /** * Determines which of the swift dependencies should be considered the * primary. * * @param dependency1 the first swift dependency to compare * @param dependency2 the second swift dependency to compare * @return the primary swift dependency */ protected Dependency getMainSwiftDependency(Dependency dependency1, Dependency dependency2) { if (dependency1 != null && dependency2 != null && Ecosystem.IOS.equals(dependency1.getEcosystem()) && Ecosystem.IOS.equals(dependency2.getEcosystem()) && isSameSwiftPackage(dependency1, dependency2)) { if (dependency1.getFileName().endsWith(".podspec")) { return dependency1; } return dependency2; } return null; } /** * Determines which of the android dependencies should be considered the * primary. * * @param dependency1 the first android dependency to compare * @param dependency2 the second android dependency to compare * @return the primary swift dependency */ protected Dependency getMainAndroidDependency(Dependency dependency1, Dependency dependency2) { if (!dependency1.isVirtual() && !dependency2.isVirtual() && Ecosystem.JAVA.equals(dependency1.getEcosystem()) && Ecosystem.JAVA.equals(dependency2.getEcosystem())) { final String name1 = dependency1.getActualFile().getName(); final String name2 = dependency2.getActualFile().getName(); if ("classes.jar".equals(name2) && "aar".equals(FileUtils.getFileExtension(name1)) && dependency2.getFileName().contains(name1)) { return dependency1; } if ("classes.jar".equals(name1) && "aar".equals(FileUtils.getFileExtension(name2)) && dependency1.getFileName().contains(name2)) { return dependency2; } } return null; } /** * Determines which of the dotnet dependencies should be considered the * primary. * * @param dependency1 the first dotnet dependency to compare * @param dependency2 the second dotnet dependency to compare * @return the primary swift dependency */ protected Dependency getMainDotnetDependency(Dependency dependency1, Dependency dependency2) { if (dependency1.getName() != null && dependency1.getVersion() != null && dependency2.getName() != null && dependency2.getVersion() != null && Ecosystem.DOTNET.equals(dependency1.getEcosystem()) && Ecosystem.DOTNET.equals(dependency2.getEcosystem()) && dependency1.getName().equals(dependency2.getName()) && dependency1.getVersion().equals(dependency2.getVersion())) { if (dependency1.isVirtual()) { return dependency2; } return dependency1; } return null; } /** * Determines which of the virtual dependencies should be considered the * primary. * * @param dependency1 the first virtual dependency to compare * @param dependency2 the second virtual dependency to compare * * @return the first virtual dependency (or {code null} if they are not to * be considered mergeable virtual dependencies) */ protected Dependency getMainVirtualDependency(Dependency dependency1, Dependency dependency2) { if (dependency1.isVirtual() && dependency2.isVirtual() && dependency1.getName() != null && dependency2.getName() != null && dependency1.getVersion() != null && dependency2.getVersion() != null && dependency1.getActualFilePath() != null && dependency2.getActualFilePath() != null && dependency1.getName().equals(dependency2.getName()) && dependency1.getVersion().equals(dependency2.getVersion()) && dependency1.getActualFilePath().equals(dependency2.getActualFilePath())) { return dependency1; } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy