diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectable/util/EnumListFilter.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectable/util/EnumListFilter.java index ab2e0ec777..623cf14e9a 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectable/util/EnumListFilter.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectable/util/EnumListFilter.java @@ -87,4 +87,7 @@ public boolean shouldExclude(T enumValue) { return excludedSet.contains(enumValue); } + public boolean shouldIncludeAll() { + return excludedSet.isEmpty(); + } } diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliDetectable.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliDetectable.java index ba845c3680..17e3b8b386 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliDetectable.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliDetectable.java @@ -36,15 +36,17 @@ public class CargoCliDetectable extends Detectable { private final CargoResolver cargoResolver; private final CargoCliExtractor cargoCliExtractor; private final DetectableExecutableRunner executableRunner; + private final CargoDetectableOptions cargoDetectableOptions; private ExecutableTarget cargoExe; private File cargoToml; - public CargoCliDetectable(DetectableEnvironment environment, FileFinder fileFinder, CargoResolver cargoResolver, CargoCliExtractor cargoCliExtractor, DetectableExecutableRunner executableRunner) { + public CargoCliDetectable(DetectableEnvironment environment, FileFinder fileFinder, CargoResolver cargoResolver, CargoCliExtractor cargoCliExtractor, DetectableExecutableRunner executableRunner, CargoDetectableOptions cargoDetectableOptions) { super(environment); this.fileFinder = fileFinder; this.cargoResolver = cargoResolver; this.cargoCliExtractor = cargoCliExtractor; this.executableRunner = executableRunner; + this.cargoDetectableOptions = cargoDetectableOptions; } @Override @@ -72,7 +74,7 @@ public DetectableResult extractable() throws DetectableException { @Override public Extraction extract(ExtractionEnvironment extractionEnvironment) throws IOException, DetectableException, MissingExternalIdException, ExecutableRunnerException { try { - return cargoCliExtractor.extract(environment.getDirectory(), cargoExe, cargoToml); + return cargoCliExtractor.extract(environment.getDirectory(), cargoExe, cargoToml, cargoDetectableOptions); } catch (Exception e) { logger.error("Failed to extract Cargo dependencies.", e); return new Extraction.Builder().failure("Cargo extraction failed due to an exception: " + e.getMessage()).build(); diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliExtractor.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliExtractor.java index 841f266849..3f27fc6454 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliExtractor.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoCliExtractor.java @@ -18,7 +18,10 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; +import java.util.ArrayList; import java.util.Optional; +import java.util.Map; +import java.util.EnumMap; public class CargoCliExtractor { private static final List CARGO_TREE_COMMAND = Arrays.asList("tree", "--no-dedupe", "--prefix", "depth"); @@ -32,8 +35,12 @@ public CargoCliExtractor(DetectableExecutableRunner executableRunner, CargoDepen this.cargoTomlParser = cargoTomlParser; } - public Extraction extract(File directory, ExecutableTarget cargoExe, File cargoTomlFile) throws ExecutableFailedException, IOException { - ExecutableOutput cargoOutput = executableRunner.executeSuccessfully(ExecutableUtils.createFromTarget(directory, cargoExe, CARGO_TREE_COMMAND)); + public Extraction extract(File directory, ExecutableTarget cargoExe, File cargoTomlFile, CargoDetectableOptions cargoDetectableOptions) throws ExecutableFailedException, IOException { + List cargoTreeCommand = new ArrayList<>(CARGO_TREE_COMMAND); + + addEdgeExclusions(cargoTreeCommand, cargoDetectableOptions); + + ExecutableOutput cargoOutput = executableRunner.executeSuccessfully(ExecutableUtils.createFromTarget(directory, cargoExe, cargoTreeCommand)); List cargoTreeOutput = cargoOutput.getStandardOutputAsList(); DependencyGraph graph = cargoDependencyTransformer.transform(cargoTreeOutput); @@ -51,4 +58,24 @@ public Extraction extract(File directory, ExecutableTarget cargoExe, File cargoT .nameVersionIfPresent(projectNameVersion) .build(); } + + private void addEdgeExclusions(List cargoTreeCommand, CargoDetectableOptions options) { + Map exclusionMap = new EnumMap<>(CargoDependencyType.class); + exclusionMap.put(CargoDependencyType.NORMAL, "no-normal"); + exclusionMap.put(CargoDependencyType.BUILD, "no-build"); + exclusionMap.put(CargoDependencyType.DEV, "no-dev"); + exclusionMap.put(CargoDependencyType.PROC_MACRO, "no-proc-macro"); + + List exclusions = new ArrayList<>(); + for (Map.Entry entry : exclusionMap.entrySet()) { + if (options.getDependencyTypeFilter().shouldExclude(entry.getKey())) { + exclusions.add(entry.getValue()); + } + } + + if (!exclusions.isEmpty()) { + cargoTreeCommand.add("--edges"); + cargoTreeCommand.add(String.join(",", exclusions)); + } + } } diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoDependencyType.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoDependencyType.java new file mode 100644 index 0000000000..e8335b559d --- /dev/null +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoDependencyType.java @@ -0,0 +1,8 @@ +package com.blackduck.integration.detectable.detectables.cargo; + +public enum CargoDependencyType { + NORMAL, + BUILD, + DEV, + PROC_MACRO +} diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoDetectableOptions.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoDetectableOptions.java new file mode 100644 index 0000000000..da5333689c --- /dev/null +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoDetectableOptions.java @@ -0,0 +1,15 @@ +package com.blackduck.integration.detectable.detectables.cargo; + +import com.blackduck.integration.detectable.detectable.util.EnumListFilter; + +public class CargoDetectableOptions { + private final EnumListFilter dependencyTypeFilter; + + public CargoDetectableOptions(EnumListFilter dependencyTypeFilter) { + this.dependencyTypeFilter = dependencyTypeFilter; + } + + public EnumListFilter getDependencyTypeFilter() { + return dependencyTypeFilter; + } +} diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoExtractor.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoExtractor.java index 1fdff76c5b..db6efef86f 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoExtractor.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoExtractor.java @@ -5,9 +5,14 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import com.blackduck.integration.detectable.detectable.util.EnumListFilter; +import com.blackduck.integration.detectable.detectables.cargo.data.CargoLockPackageData; import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nullable; @@ -39,10 +44,29 @@ public CargoExtractor( this.cargoLockPackageTransformer = cargoLockPackageTransformer; } - public Extraction extract(File cargoLockFile, @Nullable File cargoTomlFile) throws IOException, DetectableException, MissingExternalIdException { + public Extraction extract(File cargoLockFile, @Nullable File cargoTomlFile, CargoDetectableOptions cargoDetectableOptions) throws IOException, DetectableException, MissingExternalIdException { CargoLockData cargoLockData = new Toml().read(cargoLockFile).to(CargoLockData.class); - List packages = cargoLockData.getPackages() - .orElse(new ArrayList<>()).stream() + List cargoLockPackageDataList = cargoLockData.getPackages().orElse(new ArrayList<>()); + List filteredPackages = cargoLockPackageDataList; + boolean exclusionEnabled = isDependencyExclusionEnabled(cargoDetectableOptions); + String cargoTomlContents = null; + + if(cargoTomlFile == null && exclusionEnabled) { + return new Extraction.Builder() + .failure("Cargo.toml file is required to exclude dependencies, but was not provided.") + .build(); + } + + if (cargoTomlFile != null) { + cargoTomlContents = FileUtils.readFileToString(cargoTomlFile, StandardCharsets.UTF_8); + } + + if (cargoTomlFile != null && exclusionEnabled) { + Map excludableDependencyMap = cargoTomlParser.parseDependenciesToExclude(cargoTomlContents, cargoDetectableOptions.getDependencyTypeFilter()); + filteredPackages = excludeDependencies(cargoLockPackageDataList, excludableDependencyMap); + } + + List packages = filteredPackages.stream() .map(cargoLockPackageDataTransformer::transform) .collect(Collectors.toList()); @@ -50,10 +74,8 @@ public Extraction extract(File cargoLockFile, @Nullable File cargoTomlFile) thro Optional projectNameVersion = Optional.empty(); if (cargoTomlFile != null) { - String cargoTomlContents = FileUtils.readFileToString(cargoTomlFile, StandardCharsets.UTF_8); projectNameVersion = cargoTomlParser.parseNameVersionFromCargoToml(cargoTomlContents); } - CodeLocation codeLocation = new CodeLocation(graph); //TODO: Consider for producing a ProjectDependencyGraph return new Extraction.Builder() @@ -61,4 +83,54 @@ public Extraction extract(File cargoLockFile, @Nullable File cargoTomlFile) thro .nameVersionIfPresent(projectNameVersion) .build(); } + + private boolean isDependencyExclusionEnabled(CargoDetectableOptions options) { + if (options == null) { + return false; + } + + EnumListFilter filter = options.getDependencyTypeFilter(); + return filter != null && !filter.shouldIncludeAll(); + } + + private List excludeDependencies( + List packages, + Map excludableDependencyMap + ) { + Set excludedNames = new HashSet<>(); + + List filtered = packages.stream() + .filter(pkg -> { + String name = pkg.getName().orElse(null); + String version = pkg.getVersion().orElse(null); + NameVersion nameVersion = new NameVersion(name, version); + if (name == null || version == null) return true; + + if (excludableDependencyMap.containsKey(nameVersion)) { + String constraint = excludableDependencyMap.get(nameVersion); + boolean matches = constraint == null || VersionUtils.versionMatches(constraint, version); + if (matches) { + excludedNames.add(name); + return false; + } + } + + return true; + }) + .collect(Collectors.toList()); + + return filtered.stream() + .map(pkg -> new CargoLockPackageData( + pkg.getName().orElse(null), + pkg.getVersion().orElse(null), + pkg.getSource().orElse(null), + pkg.getChecksum().orElse(null), + pkg.getDependencies() + .orElse(new ArrayList<>()) + .stream() + .filter(dep -> !excludedNames.contains(dep)) + .collect(Collectors.toList()) + )) + .collect(Collectors.toList()); + } } diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoLockDetectable.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoLockDetectable.java index 2f74e21596..a0bab51af1 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoLockDetectable.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/CargoLockDetectable.java @@ -25,14 +25,15 @@ public class CargoLockDetectable extends Detectable { private final FileFinder fileFinder; private final CargoExtractor cargoExtractor; - + private final CargoDetectableOptions cargoDetectableOptions; private File cargoLock; private File cargoToml; - public CargoLockDetectable(DetectableEnvironment environment, FileFinder fileFinder, CargoExtractor cargoExtractor) { + public CargoLockDetectable(DetectableEnvironment environment, FileFinder fileFinder, CargoExtractor cargoExtractor, CargoDetectableOptions cargoDetectableOptions) { super(environment); this.fileFinder = fileFinder; this.cargoExtractor = cargoExtractor; + this.cargoDetectableOptions = cargoDetectableOptions; } @Override @@ -52,6 +53,6 @@ public DetectableResult extractable() { @Override public Extraction extract(ExtractionEnvironment extractionEnvironment) throws IOException, DetectableException, MissingExternalIdException { - return cargoExtractor.extract(cargoLock, cargoToml); + return cargoExtractor.extract(cargoLock, cargoToml, cargoDetectableOptions); } } diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/VersionUtils.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/VersionUtils.java index 60d29acaa1..fd4cecdfd2 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/VersionUtils.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/VersionUtils.java @@ -14,4 +14,91 @@ public static int compareVersions(String version1, String version2) { } return 0; } + + public static boolean versionMatches(String constraint, String actualVersion) { + if (constraint == null || actualVersion == null) { + return false; + } + + String normalizedActual = normalizeVersion(actualVersion); + String normalizedConstraintVersion; + + if (constraint.startsWith(">=")) { + normalizedConstraintVersion = normalizeVersion(constraint.substring(2)); + return compareVersions(normalizedActual, normalizedConstraintVersion) >= 0; + } else if (constraint.startsWith(">")) { + normalizedConstraintVersion = normalizeVersion(constraint.substring(1)); + return compareVersions(normalizedActual, normalizedConstraintVersion) > 0; + } else if (constraint.startsWith("<=")) { + normalizedConstraintVersion = normalizeVersion(constraint.substring(2)); + return compareVersions(normalizedActual, normalizedConstraintVersion) <= 0; + } else if (constraint.startsWith("<")) { + normalizedConstraintVersion = normalizeVersion(constraint.substring(1)); + return compareVersions(normalizedActual, normalizedConstraintVersion) < 0; + } else if (constraint.startsWith("=")) { + normalizedConstraintVersion = normalizeVersion(constraint.substring(1)); + return compareVersions(normalizedActual, normalizedConstraintVersion) == 0; + } else { + return versionCompatible(constraint, actualVersion); + } + } + + private static String normalizeVersion(String version) { + String[] parts = version.split("\\."); + StringBuilder normalized = new StringBuilder(); + for (int i = 0; i < 3; i++) { + if (i < parts.length) { + normalized.append(parts[i]); + } else { + normalized.append("0"); + } + if (i < 2) { + normalized.append("."); + } + } + return normalized.toString(); + } + + public static boolean versionCompatible(String declaredVersion, String actualVersion) { + if (declaredVersion == null || actualVersion == null) { + return false; + } + + String[] declaredParts = declaredVersion.split("\\."); + String[] actualParts = actualVersion.split("\\."); + + // Fill both arrays to length 3 with "0" if needed + String[] normalizedDeclared = new String[] { + declaredParts.length > 0 ? declaredParts[0] : "0", + declaredParts.length > 1 ? declaredParts[1] : "0", + declaredParts.length > 2 ? declaredParts[2] : "0" + }; + String[] normalizedActual = new String[] { + actualParts.length > 0 ? actualParts[0] : "0", + actualParts.length > 1 ? actualParts[1] : "0", + actualParts.length > 2 ? actualParts[2] : "0" + }; + + int declaredMajor = Integer.parseInt(normalizedDeclared[0]); + int declaredMinor = Integer.parseInt(normalizedDeclared[1]); + int declaredPatch = Integer.parseInt(normalizedDeclared[2]); + + int actualMajor = Integer.parseInt(normalizedActual[0]); + int actualMinor = Integer.parseInt(normalizedActual[1]); + int actualPatch = Integer.parseInt(normalizedActual[2]); + + // Cargo behavior: + // - if 0.x.y: treat minor as the compatibility boundary + // - if >=1.0.0: treat major as the compatibility boundary + if (declaredMajor == 0) { + return actualMajor == 0 && + actualMinor == declaredMinor && + actualPatch >= declaredPatch; + } else { + return actualMajor == declaredMajor && + (actualMinor > declaredMinor || + (actualMinor == declaredMinor && actualPatch >= declaredPatch)); + } + } + } \ No newline at end of file diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/parse/CargoTomlParser.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/parse/CargoTomlParser.java index c616037c55..6499bcc456 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/parse/CargoTomlParser.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/parse/CargoTomlParser.java @@ -1,16 +1,25 @@ package com.blackduck.integration.detectable.detectables.cargo.parse; +import java.util.EnumSet; import java.util.Optional; +import java.util.Map; +import java.util.HashMap; +import com.blackduck.integration.detectable.detectable.util.EnumListFilter; +import com.blackduck.integration.detectable.detectables.cargo.CargoDependencyType; import org.tomlj.Toml; import org.tomlj.TomlParseResult; import com.blackduck.integration.util.NameVersion; +import org.tomlj.TomlTable; public class CargoTomlParser { private static final String NAME_KEY = "name"; private static final String VERSION_KEY = "version"; private static final String PACKAGE_KEY = "package"; + private static final String NORMAL_DEPENDENCIES_KEY = "dependencies"; + private static final String BUILD_DEPENDENCIES_KEY = "build-dependencies"; + private static final String DEV_DEPENDENCIES_KEY = "dev-dependencies"; public Optional parseNameVersionFromCargoToml(String tomlFileContents) { TomlParseResult cargoTomlObject = Toml.parse(tomlFileContents); @@ -22,4 +31,47 @@ public Optional parseNameVersionFromCargoToml(String tomlFileConten return Optional.empty(); } + public Map parseDependenciesToExclude(String tomlFileContents, EnumListFilter dependencyTypeFilter) { + TomlParseResult toml = Toml.parse(tomlFileContents); + Map> dependencyTypeMap = new HashMap<>(); + + parseDependenciesFromTomlTable(toml, NORMAL_DEPENDENCIES_KEY, CargoDependencyType.NORMAL, dependencyTypeMap); + parseDependenciesFromTomlTable(toml, BUILD_DEPENDENCIES_KEY, CargoDependencyType.BUILD, dependencyTypeMap); + parseDependenciesFromTomlTable(toml, DEV_DEPENDENCIES_KEY, CargoDependencyType.DEV, dependencyTypeMap); + + Map dependenciesToExclude = new HashMap<>(); + for (Map.Entry> entry : dependencyTypeMap.entrySet()) { + NameVersion nameVersion = entry.getKey(); + EnumSet types = entry.getValue(); + + boolean shouldBeExcluded = types.stream() + .allMatch(dependencyTypeFilter::shouldExclude); + + if (shouldBeExcluded) { + dependenciesToExclude.put(nameVersion, nameVersion.getVersion()); + } + } + return dependenciesToExclude; + } + + private void parseDependenciesFromTomlTable(TomlParseResult toml, String sectionKey, CargoDependencyType type, Map> dependencyTypeMap) { + TomlTable table = toml.getTable(sectionKey); + if (table == null) { + return; + } + + for (String key : table.keySet()) { + Object value = table.get(key); + String version = null; + + if (value instanceof String) { + version = (String) value; + } else if (value instanceof TomlTable) { + version = ((TomlTable) value).getString(VERSION_KEY); // May be null + } + + NameVersion nv = new NameVersion(key, version); + dependencyTypeMap.computeIfAbsent(nv, k -> EnumSet.noneOf(CargoDependencyType.class)).add(type); + } + } } diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/transform/CargoLockPackageTransformer.java b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/transform/CargoLockPackageTransformer.java index 475a75de75..164ff36ca8 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/transform/CargoLockPackageTransformer.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/detectables/cargo/transform/CargoLockPackageTransformer.java @@ -1,6 +1,8 @@ package com.blackduck.integration.detectable.detectables.cargo.transform; +import java.util.HashMap; import java.util.List; +import java.util.Map; import com.blackduck.integration.bdio.graph.DependencyGraph; import com.blackduck.integration.bdio.graph.builder.LazyExternalIdDependencyGraphBuilder; @@ -9,6 +11,7 @@ import com.blackduck.integration.bdio.model.Forge; import com.blackduck.integration.bdio.model.dependency.Dependency; import com.blackduck.integration.bdio.model.dependency.DependencyFactory; +import com.blackduck.integration.bdio.model.externalid.ExternalId; import com.blackduck.integration.bdio.model.externalid.ExternalIdFactory; import com.blackduck.integration.detectable.detectable.exception.DetectableException; import com.blackduck.integration.detectable.detectables.cargo.model.CargoLockPackage; @@ -26,15 +29,21 @@ public DependencyGraph transformToGraph(List lockPackages) thr String parentName = lockPackage.getPackageNameVersion().getName(); String parentVersion = lockPackage.getPackageNameVersion().getVersion(); LazyId parentId = LazyId.fromNameAndVersion(parentName, parentVersion); - Dependency parentDependency = dependencyFactory.createNameVersionDependency(Forge.CRATES, parentName, parentVersion); + ExternalId parentExternalId = externalIdFactory.createNameVersionExternalId(Forge.CRATES, parentName, parentVersion); - graph.addChildToRoot(parentId); - graph.setDependencyInfo(parentId, parentDependency.getName(), parentDependency.getVersion(), parentDependency.getExternalId()); + graph.setDependencyInfo(parentId, parentName, parentVersion, parentExternalId); graph.setDependencyAsAlias(parentId, LazyId.fromName(parentName)); + graph.addChildToRoot(parentId); lockPackage.getDependencies().forEach(childPackage -> { if (childPackage.getVersion().isPresent()) { - LazyId childId = LazyId.fromNameAndVersion(childPackage.getName(), childPackage.getVersion().get()); + String childName = childPackage.getName(); + String childVersion = childPackage.getVersion().get(); + LazyId childId = LazyId.fromNameAndVersion(childName, childVersion); + ExternalId childExternalId = externalIdFactory.createNameVersionExternalId(Forge.CRATES, childName, childVersion); + + graph.setDependencyInfo(childId, childName, childVersion, childExternalId); + graph.setDependencyAsAlias(childId, LazyId.fromName(childName)); graph.addChildWithParent(childId, parentId); } else { LazyId childId = LazyId.fromName(childPackage.getName()); diff --git a/detectable/src/main/java/com/blackduck/integration/detectable/factory/DetectableFactory.java b/detectable/src/main/java/com/blackduck/integration/detectable/factory/DetectableFactory.java index fa8c9b688e..ec13034859 100644 --- a/detectable/src/main/java/com/blackduck/integration/detectable/factory/DetectableFactory.java +++ b/detectable/src/main/java/com/blackduck/integration/detectable/factory/DetectableFactory.java @@ -348,19 +348,23 @@ public BitbakeDetectable createBitbakeDetectable(DetectableEnvironment environme } public CargoLockDetectable createCargoDetectable(DetectableEnvironment environment) { + return createCargoDetectable(environment, null); + } + + public CargoLockDetectable createCargoDetectable(DetectableEnvironment environment, CargoDetectableOptions cargoDetectableOptions) { CargoTomlParser cargoTomlParser = new CargoTomlParser(); CargoDependencyLineParser cargoDependencyLineParser = new CargoDependencyLineParser(); CargoLockPackageDataTransformer cargoLockPackageDataTransformer = new CargoLockPackageDataTransformer(cargoDependencyLineParser); CargoLockPackageTransformer cargoLockPackageTransformer = new CargoLockPackageTransformer(); CargoExtractor cargoExtractor = new CargoExtractor(cargoTomlParser, cargoLockPackageDataTransformer, cargoLockPackageTransformer); - return new CargoLockDetectable(environment, fileFinder, cargoExtractor); + return new CargoLockDetectable(environment, fileFinder, cargoExtractor, cargoDetectableOptions); } - public CargoCliDetectable createCargoCliDetectable(DetectableEnvironment environment, CargoResolver cargoResolver) { + public CargoCliDetectable createCargoCliDetectable(DetectableEnvironment environment, CargoResolver cargoResolver, CargoDetectableOptions cargoDetectableOptions) { CargoDependencyGraphTransformer cargoDependencyTransformer= new CargoDependencyGraphTransformer(externalIdFactory); CargoTomlParser cargoTomlParser = new CargoTomlParser(); CargoCliExtractor cargoCliExtractor = new CargoCliExtractor(executableRunner, cargoDependencyTransformer, cargoTomlParser); - return new CargoCliDetectable(environment, fileFinder, cargoResolver, cargoCliExtractor, executableRunner); + return new CargoCliDetectable(environment, fileFinder, cargoResolver, cargoCliExtractor, executableRunner, cargoDetectableOptions); } diff --git a/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java b/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java index 5bb985ce5f..3acd33cb4b 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java @@ -55,6 +55,7 @@ import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedSnippetMode; import com.blackduck.integration.detectable.detectables.bazel.WorkspaceRule; import com.blackduck.integration.detectable.detectables.bitbake.BitbakeDependencyType; +import com.blackduck.integration.detectable.detectables.cargo.CargoDependencyType; import com.blackduck.integration.detectable.detectables.conan.cli.config.ConanDependencyType; import com.blackduck.integration.detectable.detectables.dart.pubdep.DartPubDependencyType; import com.blackduck.integration.detectable.detectables.go.gomod.GoModDependencyType; @@ -609,6 +610,17 @@ private DetectProperties() { .setGroups(DetectGroup.DART, DetectGroup.GLOBAL) .build(); + public static final NoneEnumListProperty DETECT_CARGO_DEPENDENCY_TYPES_EXCLUDED = + NoneEnumListProperty.newBuilder("detect.cargo.dependency.types.excluded", NoneEnum.NONE, CargoDependencyType.class) + .setInfo("Cargo Dependency Types Excluded", DetectPropertyFromVersion.VERSION_10_5_0) + .setHelp( + "A comma-separated list of dependency types that will be excluded.", + "The Cargo CLI Detector uses cargo tree flags to exclude the specified types, while the Cargo Lockfile Detector filters dependencies by reading Cargo.toml. For example, passing `detect.cargo.dependency.types.excluded=DEV` will skip [dev-dependencies] from detection." + ) + .setExample(CargoDependencyType.DEV.name()) + .setGroups(DetectGroup.CARGO, DetectGroup.DETECTOR, DetectGroup.GLOBAL) + .build(); + public static final NoneEnumListProperty DETECT_PIPFILE_DEPENDENCY_TYPES_EXCLUDED = NoneEnumListProperty.newBuilder("detect.pipfile.dependency.types.excluded", NoneEnum.NONE, PipenvDependencyType.class) .setInfo("Pipfile Dependency Types Excluded", DetectPropertyFromVersion.VERSION_7_13_0) diff --git a/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java b/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java index 0afd9defc2..549be48890 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.Set; +import com.blackduck.integration.detectable.detectables.cargo.CargoDetectableOptions; +import com.blackduck.integration.detectable.detectables.cargo.CargoDependencyType; import com.blackduck.integration.detectable.detectables.nuget.NugetDependencyType; import com.blackduck.integration.detectable.detectables.uv.UVDetectorOptions; import org.jetbrains.annotations.Nullable; @@ -229,6 +231,12 @@ public PearCliDetectableOptions createPearCliDetectableOptions() { return new PearCliDetectableOptions(pearDependencyTypeFilter); } + public CargoDetectableOptions createCargoDetectableOptions() { + Set excludedDependencyTypes = detectConfiguration.getValue(DetectProperties.DETECT_CARGO_DEPENDENCY_TYPES_EXCLUDED).representedValueSet(); + EnumListFilter dependencyTypeFilter = EnumListFilter.fromExcluded(excludedDependencyTypes); + return new CargoDetectableOptions(dependencyTypeFilter); + } + public PipenvDetectableOptions createPipenvDetectableOptions() { String pipProjectName = detectConfiguration.getNullableValue(DetectProperties.DETECT_PIP_PROJECT_NAME); String pipProjectVersionName = detectConfiguration.getNullableValue(DetectProperties.DETECT_PIP_PROJECT_VERSION_NAME); diff --git a/src/main/java/com/blackduck/integration/detect/tool/detector/factory/DetectDetectableFactory.java b/src/main/java/com/blackduck/integration/detect/tool/detector/factory/DetectDetectableFactory.java index 003be68cac..f3ec182ac5 100644 --- a/src/main/java/com/blackduck/integration/detect/tool/detector/factory/DetectDetectableFactory.java +++ b/src/main/java/com/blackduck/integration/detect/tool/detector/factory/DetectDetectableFactory.java @@ -120,11 +120,11 @@ public BitbakeDetectable createBitbakeDetectable(DetectableEnvironment environme } public CargoLockDetectable createCargoDetectable(DetectableEnvironment environment) { - return detectableFactory.createCargoDetectable(environment); + return detectableFactory.createCargoDetectable(environment, detectableOptionFactory.createCargoDetectableOptions()); } public CargoCliDetectable createCargoCliDetectable(DetectableEnvironment environment) { - return detectableFactory.createCargoCliDetectable(environment, detectExecutableResolver); + return detectableFactory.createCargoCliDetectable(environment, detectExecutableResolver, detectableOptionFactory.createCargoDetectableOptions()); } public CarthageLockDetectable createCarthageDetectable(DetectableEnvironment environment) {