diff --git a/documentation/src/main/markdown/currentreleasenotes.md b/documentation/src/main/markdown/currentreleasenotes.md index 5ea96bdd3e..20562f19a9 100644 --- a/documentation/src/main/markdown/currentreleasenotes.md +++ b/documentation/src/main/markdown/currentreleasenotes.md @@ -26,8 +26,13 @@ * Support for pnpm now extends to 10.32.1. * npm detectors now allow for aliases to be used when specifying dependencies in the package.json file. * Ivy CLI Detector, leveraging the `ivy:dependencytree` Ant task to extract direct and transitive dependencies for Ant + Ivy projects. For further information, see [Ivy (Ant) support](packagemgrs/ivy.md). +* Introduced the `detect.quack.patch.output property` to control the Quack Patch output information path. If not set, the current working directory will be used as the default. * When [detect_product_short] is integrated with [bd_product_long] version 2026.4 or later, relevant [bd_product_short] server configuration details will be retrieved for use by [detect_product_short]. With this release of [detect_product_short], the [bd_product_short] server administrator can choose to set the detect.blackduck.correlated.scanning.enabled property, which will be retrieved and used if the user has not specified this property locally. - * In future releases the retrieval of additional [bd_product_short] server properties will be supported. + * In future releases the retrieval of additional [bd_product_short] server properties will be supported. + +### Changed features + +* The default output directory of the Quack Patch feature has been updated to use a quack-patch subdirectory of the present working directory. ### Resolved issues 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 812aeec0a0..731f75ce9a 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java @@ -1,12 +1,12 @@ package com.blackduck.integration.detect.configuration; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; - import java.lang.reflect.Field; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; @@ -21,7 +21,6 @@ import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.SnippetMatching; import com.blackduck.integration.configuration.property.Properties; import com.blackduck.integration.configuration.property.Property; -import com.blackduck.integration.configuration.util.ProductMajorVersion; import com.blackduck.integration.configuration.property.base.PassthroughProperty; import com.blackduck.integration.configuration.property.base.TypedProperty; import com.blackduck.integration.configuration.property.types.bool.BooleanProperty; @@ -44,6 +43,7 @@ import com.blackduck.integration.configuration.property.types.string.NullableStringProperty; import com.blackduck.integration.configuration.property.types.string.StringListProperty; import com.blackduck.integration.configuration.property.types.string.StringProperty; +import com.blackduck.integration.configuration.util.ProductMajorVersion; import com.blackduck.integration.detect.configuration.enumeration.BlackduckScanMode; import com.blackduck.integration.detect.configuration.enumeration.DetectCategory; import com.blackduck.integration.detect.configuration.enumeration.DetectGroup; @@ -53,8 +53,8 @@ import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedIndividualFileMatchingMode; import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedReducedPersistanceMode; import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedSnippetMode; -import com.blackduck.integration.detectable.detectables.bazel.WorkspaceRule; import com.blackduck.integration.detectable.detectables.bazel.DependencySource; +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; @@ -1146,6 +1146,16 @@ private DetectProperties() { "Only supported for Rapid and Stateless Scan modes. detect.llm.api.key, detect.llm.api.endpoint, and detect.llm.name must also be set. See Quack Patch for further details.") .setGroups(DetectGroup.QUACKPATCH) .build(); + + public static final StringProperty DETECT_QUACK_PATCH_OUTPUT = + StringProperty.newBuilder("detect.quack.patch.output", Path.of("").toAbsolutePath().toString()) + .setInfo("Quack Patch Output Directory", DetectPropertyFromVersion.VERSION_11_4_0) + .setHelp( + "Specifies the output directory for Quack Patch results.", + "If not set, the Quack Patch results are placed in a 'quack-patch' subdirectory of the present working directory." + ) + .setGroups(DetectGroup.QUACKPATCH) + .build(); public static final StringProperty DETECT_LLM_API_KEY = StringProperty.newBuilder("detect.llm.api.key", "") diff --git a/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java b/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java index 7137907c44..ebe4e4968e 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java @@ -1,5 +1,7 @@ package com.blackduck.integration.detect.configuration.validation; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -17,6 +19,7 @@ import com.blackduck.integration.configuration.property.base.TypedProperty; import com.blackduck.integration.configuration.property.deprecation.DeprecatedValueUsage; import com.blackduck.integration.detect.configuration.DetectProperties; +import com.blackduck.integration.detect.configuration.DetectPropertyConfiguration; import com.blackduck.integration.detect.configuration.DetectUserFriendlyException; import com.blackduck.integration.detect.configuration.enumeration.ExitCodeType; import com.blackduck.integration.detect.workflow.event.EventSystem; @@ -102,4 +105,48 @@ public Optional validateForPropertyParseErrors() th } return Optional.empty(); } + + // Method to validate Quack Patch output path and return an Optional containing a DetectUserFriendlyException if the validation fails, or an empty Optional if it passes. + public Optional validateQuackPatchOutputPath(DetectPropertyConfiguration detectConfiguration) { + String quackPatchOutput = detectConfiguration.getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim(); + + // Fail for empty string since that would cause issues later on when we try to write to it, and it's likely the user just forgot to set it if they enabled Quack Patch but left this blank. + if (quackPatchOutput.isEmpty()) { + return Optional.of(new DetectUserFriendlyException( + "Invalid value for Quack Patch output path: " + quackPatchOutput, + ExitCodeType.FAILURE_CONFIGURATION + )); + } + // Validate the path exists and is a directory + Path path = Path.of(quackPatchOutput); + + if (!Files.exists(path)) { + // create the directory if it doesn't exist, since that would cause issues later on when we try to write to it, and it's likely the user just forgot to create it if they enabled Quack Patch and set a path that doesn't exist. + try { + logger.debug("Creating quack patch output path: {}", quackPatchOutput); + Files.createDirectories(path); + } catch (Exception e) { + return Optional.of(new DetectUserFriendlyException( + "Quack Patch output path does not exist and could not be created: " + quackPatchOutput, + ExitCodeType.FAILURE_CONFIGURATION + )); + } + } + + if (!Files.isDirectory(path)) { + return Optional.of(new DetectUserFriendlyException( + "Quack Patch output path is not a directory: " + quackPatchOutput, + ExitCodeType.FAILURE_CONFIGURATION + )); + } + + if (!Files.isWritable(path)) { + return Optional.of(new DetectUserFriendlyException( + "Quack Patch output path is not writable: " + quackPatchOutput, + ExitCodeType.FAILURE_CONFIGURATION + )); + } + + return Optional.empty(); + } } diff --git a/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java b/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java index b5e684877f..7fc331d64f 100644 --- a/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java +++ b/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java @@ -5,28 +5,24 @@ import java.io.PrintStream; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; -import java.util.Collections; -import java.util.LinkedHashMap; -import com.blackduck.integration.configuration.config.MaskedRawValueResult; -import com.blackduck.integration.configuration.property.base.TypedProperty; -import com.blackduck.integration.configuration.property.types.enumallnone.list.AllEnumList; -import com.blackduck.integration.configuration.property.types.path.PathValue; -import com.blackduck.integration.detect.configuration.connection.BlackDuckConnectionDetails; -import com.blackduck.integration.detect.configuration.help.yaml.HelpYamlWriter; -import com.blackduck.integration.detect.workflow.status.DetectIssue; -import com.blackduck.integration.detect.workflow.status.DetectIssueType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; +import com.blackduck.integration.configuration.config.MaskedRawValueResult; import com.blackduck.integration.configuration.config.PropertyConfiguration; +import com.blackduck.integration.configuration.property.base.TypedProperty; +import com.blackduck.integration.configuration.property.types.enumallnone.list.AllEnumList; +import com.blackduck.integration.configuration.property.types.path.PathValue; import com.blackduck.integration.configuration.property.types.path.SimplePathResolver; import com.blackduck.integration.configuration.source.MapPropertySource; import com.blackduck.integration.configuration.source.PropertySource; @@ -36,6 +32,7 @@ import com.blackduck.integration.detect.configuration.DetectPropertyUtil; import com.blackduck.integration.detect.configuration.DetectUserFriendlyException; import com.blackduck.integration.detect.configuration.DetectableOptionFactory; +import com.blackduck.integration.detect.configuration.connection.BlackDuckConnectionDetails; import com.blackduck.integration.detect.configuration.enumeration.BlackduckScanMode; import com.blackduck.integration.detect.configuration.enumeration.DetectGroup; import com.blackduck.integration.detect.configuration.enumeration.DetectTargetType; @@ -44,6 +41,7 @@ import com.blackduck.integration.detect.configuration.help.DetectArgumentState; import com.blackduck.integration.detect.configuration.help.json.HelpJsonManager; import com.blackduck.integration.detect.configuration.help.print.HelpPrinter; +import com.blackduck.integration.detect.configuration.help.yaml.HelpYamlWriter; import com.blackduck.integration.detect.configuration.validation.DeprecationResult; import com.blackduck.integration.detect.configuration.validation.DetectConfigurationBootManager; import com.blackduck.integration.detect.interactive.InteractiveManager; @@ -55,22 +53,24 @@ import com.blackduck.integration.detect.lifecycle.boot.product.ProductBoot; import com.blackduck.integration.detect.lifecycle.run.data.ProductRunData; import com.blackduck.integration.detect.lifecycle.run.singleton.BootSingletons; -import com.blackduck.integration.detect.workflow.blackduck.settings.DetectPropertiesSetting; import com.blackduck.integration.detect.tool.cache.InstalledToolLocator; import com.blackduck.integration.detect.tool.cache.InstalledToolManager; import com.blackduck.integration.detect.util.filter.DetectToolFilter; import com.blackduck.integration.detect.workflow.airgap.AirGapCreator; import com.blackduck.integration.detect.workflow.airgap.AirGapType; import com.blackduck.integration.detect.workflow.airgap.AirGapTypeDecider; +import com.blackduck.integration.detect.workflow.blackduck.settings.DetectPropertiesSetting; import com.blackduck.integration.detect.workflow.diagnostic.DiagnosticDecision; import com.blackduck.integration.detect.workflow.diagnostic.DiagnosticSystem; import com.blackduck.integration.detect.workflow.event.Event; import com.blackduck.integration.detect.workflow.event.EventSystem; import com.blackduck.integration.detect.workflow.file.DirectoryManager; +import com.blackduck.integration.detect.workflow.status.DetectIssue; +import com.blackduck.integration.detect.workflow.status.DetectIssueType; import com.blackduck.integration.rest.proxy.ProxyInfo; +import com.google.gson.Gson; import freemarker.template.Configuration; -import java.util.Set; public class DetectBoot { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -161,6 +161,11 @@ public Optional boot(String detectVersion, String detectBuildD Configuration freemarkerConfiguration = detectBootFactory.createFreemarkerConfiguration(); DetectPropertyConfiguration detectConfiguration = new DetectPropertyConfiguration(propertyConfiguration, new SimplePathResolver()); + // If quack patch is enabled, we need to validate the output path before doing anything else since it could cause Detect to fail later on if it's not valid, and we want to fail as early as possible with a clear message about what the issue is. + Optional quackPatchError = detectConfigurationBootManager.validateQuackPatchOutputPath(detectConfiguration); + if (quackPatchError.isPresent()) { + return Optional.of(DetectBootResult.exception(quackPatchError.get(), propertyConfiguration)); + } DetectConfigurationFactory detectConfigurationFactory = new DetectConfigurationFactory(detectConfiguration, gson); diff --git a/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java b/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java index 351d39caa5..95097f614a 100644 --- a/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java +++ b/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java @@ -1,10 +1,5 @@ package com.blackduck.integration.detect.lifecycle.run.operation; -import static com.blackduck.integration.blackduck.codelocation.signaturescanner.command.ToolsApiScannerInstaller.MIN_ARM_BLACK_DUCK_VERSION; -import static com.blackduck.integration.componentlocator.ComponentLocator.SUPPORTED_DETECTORS; -import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.OPERATION_NAME; -import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.SUPPORTED_DETECTORS_LOG_MSG; - import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; @@ -61,6 +56,7 @@ import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.ScanCommandOutput; import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.ScanCommandRunner; import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.ScanPathsUtility; +import static com.blackduck.integration.blackduck.codelocation.signaturescanner.command.ToolsApiScannerInstaller.MIN_ARM_BLACK_DUCK_VERSION; import com.blackduck.integration.blackduck.http.BlackDuckRequestBuilder; import com.blackduck.integration.blackduck.service.BlackDuckApiClient; import com.blackduck.integration.blackduck.service.BlackDuckServicesFactory; @@ -69,9 +65,11 @@ import com.blackduck.integration.blackduck.service.request.BlackDuckResponseRequest; import com.blackduck.integration.blackduck.version.BlackDuckVersion; import com.blackduck.integration.common.util.finder.FileFinder; +import static com.blackduck.integration.componentlocator.ComponentLocator.SUPPORTED_DETECTORS; import com.blackduck.integration.componentlocator.beans.Component; import com.blackduck.integration.detect.configuration.DetectConfigurationFactory; import com.blackduck.integration.detect.configuration.DetectInfo; +import com.blackduck.integration.detect.configuration.DetectProperties; import com.blackduck.integration.detect.configuration.DetectUserFriendlyException; import com.blackduck.integration.detect.configuration.DetectorToolOptions; import com.blackduck.integration.detect.configuration.connection.ConnectionFactory; @@ -82,7 +80,6 @@ import com.blackduck.integration.detect.lifecycle.OperationException; import com.blackduck.integration.detect.lifecycle.autonomous.AutonomousManager; import com.blackduck.integration.detect.lifecycle.boot.decision.CorrelatedScanningDecision; -import com.blackduck.integration.detect.workflow.DetectRunId; import com.blackduck.integration.detect.lifecycle.run.DetectFontLoaderFactory; import com.blackduck.integration.detect.lifecycle.run.data.BlackDuckRunData; import com.blackduck.integration.detect.lifecycle.run.data.DockerTargetData; @@ -110,7 +107,6 @@ import com.blackduck.integration.detect.tool.detector.executable.DetectExecutableRunner; import com.blackduck.integration.detect.tool.detector.extraction.ExtractionEnvironmentProvider; import com.blackduck.integration.detect.tool.detector.factory.DetectDetectableFactory; -import com.blackduck.integration.detector.accuracy.detectable.DetectableExclusionEvaluator; import com.blackduck.integration.detect.tool.iac.CalculateIacScanTargetsOperation; import com.blackduck.integration.detect.tool.iac.IacScanOperation; import com.blackduck.integration.detect.tool.iac.IacScanReport; @@ -139,6 +135,7 @@ import com.blackduck.integration.detect.util.bdio.protobuf.DetectProtobufBdioHeaderUtil; import com.blackduck.integration.detect.util.finder.DetectExcludedDirectoryFilter; import com.blackduck.integration.detect.workflow.ArtifactResolver; +import com.blackduck.integration.detect.workflow.DetectRunId; import com.blackduck.integration.detect.workflow.bdio.AggregateCodeLocation; import com.blackduck.integration.detect.workflow.bdio.BdioResult; import com.blackduck.integration.detect.workflow.bdio.CreateAggregateBdio2FileOperation; @@ -189,6 +186,8 @@ import com.blackduck.integration.detect.workflow.codelocation.DetectCodeLocation; import com.blackduck.integration.detect.workflow.componentlocationanalysis.BdioToComponentListTransformer; import com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation; +import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.OPERATION_NAME; +import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.SUPPORTED_DETECTORS_LOG_MSG; import com.blackduck.integration.detect.workflow.componentlocationanalysis.ScanResultToComponentListTransformer; import com.blackduck.integration.detect.workflow.event.EventSystem; import com.blackduck.integration.detect.workflow.file.DirectoryManager; @@ -206,6 +205,7 @@ import com.blackduck.integration.detect.workflow.status.StatusEventPublisher; import com.blackduck.integration.detect.workflow.status.StatusType; import com.blackduck.integration.detector.accuracy.detectable.DetectableEvaluator; +import com.blackduck.integration.detector.accuracy.detectable.DetectableExclusionEvaluator; import com.blackduck.integration.detector.accuracy.directory.DirectoryEvaluator; import com.blackduck.integration.detector.accuracy.entrypoint.DetectorRuleEvaluator; import com.blackduck.integration.detector.accuracy.search.SearchEvaluator; @@ -385,7 +385,8 @@ public final DetectorToolResult executeDetectors() throws OperationException { statusEventPublisher, exitCodePublisher, detectorEventPublisher, - directoryEvaluator + directoryEvaluator, + detectConfigurationFactory ); DetectorToolResult toolResult = detectorTool.performDetectors( directoryManager, @@ -399,7 +400,7 @@ public final DetectorToolResult executeDetectors() throws OperationException { if (detectConfigurationFactory.isQuackPatchPossible()) { try { - detectorTool.saveExtractedDetectorsAndTheirRelevantFilePaths(directoryManager, toolResult); + detectorTool.saveExtractedDetectorsAndTheirRelevantFilePaths(toolResult); } catch (IOException e) { throw new RuntimeException("Something went wrong writing relevant files: " + e.getMessage()); } @@ -870,7 +871,7 @@ public final File generateFullRapidJsonFile(List scanResults) throws O return auditLog.namedPublic( "Generate Rapid Full Json File", "RapidScan", - () -> new RapidModeGenerateJsonOperation(htmlEscapeDisabledGson, directoryManager).generateJsonFileFromString(scanResults.get(0).getContentString()) + () -> new RapidModeGenerateJsonOperation(htmlEscapeDisabledGson, directoryManager).generateJsonFileFromString(scanResults.get(0).getContentString(), detectConfigurationFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim()) ); } @@ -884,7 +885,7 @@ public void runQuackPatch(File rapidFullResultsJson) throws OperationException { () -> { publishResult( new GenerateComponentLocationAnalysisOperation(detectConfigurationFactory, statusEventPublisher, exitCodePublisher) - .runQuackPatch(directoryManager.getScanOutputDirectory(), rapidFullResultsJson, detectConfigurationFactory) + .runQuackPatch(rapidFullResultsJson, detectConfigurationFactory) ); } ); diff --git a/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java b/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java index 8b3d96c3fc..e00f3f5752 100644 --- a/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java +++ b/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java @@ -1,25 +1,28 @@ package com.blackduck.integration.detect.tool.detector; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import com.blackduck.integration.detect.tool.detector.report.detectable.ExtractedDetectableReport; -import com.blackduck.integration.detect.workflow.file.DirectoryManager; -import com.blackduck.integration.detector.base.DetectorStatusCode; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.blackduck.integration.blackduck.bdio2.model.GitInfo; import com.blackduck.integration.common.util.finder.FileFinder; +import com.blackduck.integration.detect.configuration.DetectConfigurationFactory; +import com.blackduck.integration.detect.configuration.DetectProperties; import com.blackduck.integration.detect.configuration.ExcludeIncludeEnumFilter; import com.blackduck.integration.detect.configuration.enumeration.ExitCodeType; import com.blackduck.integration.detect.lifecycle.shutdown.ExitCodePublisher; @@ -28,6 +31,9 @@ import com.blackduck.integration.detect.tool.detector.report.rule.ExtractedDetectorRuleReport; import com.blackduck.integration.detect.tool.detector.report.util.DetectorReporter; import com.blackduck.integration.detect.workflow.codelocation.DetectCodeLocation; +import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON; +import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.QUACKPATCH_SUBDIRECTORY_NAME; +import com.blackduck.integration.detect.workflow.file.DirectoryManager; import com.blackduck.integration.detect.workflow.git.DetectorGitProjectInfoDecider; import com.blackduck.integration.detect.workflow.nameversion.DetectorEvaluationNameVersionDecider; import com.blackduck.integration.detect.workflow.nameversion.DetectorNameVersionDecider; @@ -42,6 +48,7 @@ import com.blackduck.integration.detectable.detectable.codelocation.CodeLocation; import com.blackduck.integration.detector.accuracy.directory.DirectoryEvaluation; import com.blackduck.integration.detector.accuracy.directory.DirectoryEvaluator; +import com.blackduck.integration.detector.base.DetectorStatusCode; import com.blackduck.integration.detector.base.DetectorType; import com.blackduck.integration.detector.finder.DirectoryFindResult; import com.blackduck.integration.detector.finder.DirectoryFinder; @@ -50,9 +57,8 @@ import com.blackduck.integration.detector.rule.DetectorRule; import com.blackduck.integration.detector.rule.DetectorRuleSet; import com.blackduck.integration.util.NameVersion; - -import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON; -import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.QUACKPATCH_SUBDIRECTORY_NAME; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; public class DetectorTool { private static final String THREE_TABS = "\t\t\t"; @@ -67,6 +73,7 @@ public class DetectorTool { private final ExitCodePublisher exitCodePublisher; private final DetectorEventPublisher detectorEventPublisher; private final DirectoryEvaluator directoryEvaluator; + private final DetectConfigurationFactory detectConfigurationFactory; public DetectorTool( DirectoryFinder directoryFinder, @@ -75,7 +82,8 @@ public DetectorTool( StatusEventPublisher statusEventPublisher, ExitCodePublisher exitCodePublisher, DetectorEventPublisher detectorEventPublisher, - DirectoryEvaluator directoryEvaluator + DirectoryEvaluator directoryEvaluator, + DetectConfigurationFactory detectConfigurationFactory ) { this.directoryFinder = directoryFinder; this.codeLocationConverter = codeLocationConverter; @@ -84,13 +92,13 @@ public DetectorTool( this.exitCodePublisher = exitCodePublisher; this.detectorEventPublisher = detectorEventPublisher; this.directoryEvaluator = directoryEvaluator; + this.detectConfigurationFactory = detectConfigurationFactory; } - public void saveExtractedDetectorsAndTheirRelevantFilePaths(DirectoryManager directoryManager, DetectorToolResult toolResult) throws IOException { + public void saveExtractedDetectorsAndTheirRelevantFilePaths(DetectorToolResult toolResult) throws IOException { // Create map of extracted detectors and their relevant files Map> detectorsAndFiles = new HashMap<>(); - Path workingDir = directoryManager.getScanOutputDirectory().toPath(); - Path quackDir = workingDir.resolve(QUACKPATCH_SUBDIRECTORY_NAME); + Path quackDir = Path.of(detectConfigurationFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim(), QUACKPATCH_SUBDIRECTORY_NAME); ObjectMapper mapper = new ObjectMapper(); Path jsonFile = quackDir.resolve(INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON); diff --git a/src/main/java/com/blackduck/integration/detect/workflow/blackduck/developer/RapidModeGenerateJsonOperation.java b/src/main/java/com/blackduck/integration/detect/workflow/blackduck/developer/RapidModeGenerateJsonOperation.java index 94e489a948..5927f64397 100644 --- a/src/main/java/com/blackduck/integration/detect/workflow/blackduck/developer/RapidModeGenerateJsonOperation.java +++ b/src/main/java/com/blackduck/integration/detect/workflow/blackduck/developer/RapidModeGenerateJsonOperation.java @@ -32,9 +32,9 @@ public RapidModeGenerateJsonOperation(Gson gson, DirectoryManager directoryManag this.directoryManager = directoryManager; } - public File generateJsonFileFromString(String jsonRapidFullResults) { + public File generateJsonFileFromString(String jsonRapidFullResults, String outputDir) { // Create the path to the subdirectory - File quackSubDir = new File(directoryManager.getScanOutputDirectory(), QUACKPATCH_SUBDIRECTORY_NAME); + File quackSubDir = new File(outputDir, QUACKPATCH_SUBDIRECTORY_NAME); try { if (!quackSubDir.exists()) { diff --git a/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java b/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java index db63d514c8..aea3f1fedf 100644 --- a/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java +++ b/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java @@ -2,16 +2,10 @@ import java.io.File; import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.Map; import java.util.Set; -import com.blackduck.integration.detect.workflow.file.DirectoryManager; -import com.blackduck.integration.detect.workflow.result.QuackPatchResult; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,9 +22,12 @@ import com.blackduck.integration.detect.workflow.file.DetectFileUtils; import com.blackduck.integration.detect.workflow.report.util.ReportConstants; import com.blackduck.integration.detect.workflow.result.ComponentLocatorResult; +import com.blackduck.integration.detect.workflow.result.QuackPatchResult; import com.blackduck.integration.detect.workflow.status.Status; import com.blackduck.integration.detect.workflow.status.StatusEventPublisher; import com.blackduck.integration.detect.workflow.status.StatusType; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; @@ -88,15 +85,17 @@ public ComponentLocatorResult locateComponents(Set componentsSet, Fil return new ComponentLocatorResult(outputFilepath); } - public QuackPatchResult runQuackPatch(File scanOutputFolder, File rapidFullResultsFile, DetectConfigurationFactory configFactory) { + public QuackPatchResult runQuackPatch(File rapidFullResultsFile, DetectConfigurationFactory configFactory) { logger.info("Attempting Quack Patch."); - Map> relevantDetectorsAndFiles = loadDetectorsAndFiles(getQuackPatchOutputDirectory(scanOutputFolder) + File.separator + INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON); - String llmKey = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_KEY); - String llmName = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_NAME); - String llmURL = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_ENDPOINT); - - ComponentLocator.runQuackPatch(rapidFullResultsFile, relevantDetectorsAndFiles, llmKey, llmName, llmURL, scanOutputFolder.getPath()); - return new QuackPatchResult(getQuackPatchOutputDirectory(scanOutputFolder)); + // if the detect.quack.patch.output property value set use that as output folder, otherwise default it to current working directory + String quackPatchOutputDir = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim(); + logger.debug("Quack Patch output directory set to: {}", quackPatchOutputDir); + Map> relevantDetectorsAndFiles = loadDetectorsAndFiles(quackPatchOutputDir + File.separator + QUACKPATCH_SUBDIRECTORY_NAME + File.separator + INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON); + String llmKey = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_KEY); + String llmName = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_NAME); + String llmURL = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_ENDPOINT); + ComponentLocator.runQuackPatch(rapidFullResultsFile, relevantDetectorsAndFiles, llmKey, llmName, llmURL, quackPatchOutputDir); + return new QuackPatchResult(getQuackPatchOutputDirectory(new File(quackPatchOutputDir))); } diff --git a/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java b/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java index 7d1b883386..830de8e066 100644 --- a/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java +++ b/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java @@ -1,11 +1,17 @@ package com.blackduck.integration.detect.workflow.report; +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.Test; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; + +import com.blackduck.integration.detect.configuration.DetectConfigurationFactory; +import com.blackduck.integration.detect.configuration.DetectConfigurationFactoryTestUtils; +import com.blackduck.integration.detect.configuration.DetectProperties; +import com.blackduck.integration.detect.configuration.DetectPropertyConfiguration; import com.blackduck.integration.detect.tool.detector.DetectorTool; import com.blackduck.integration.detect.tool.detector.DetectorToolResult; import com.blackduck.integration.detect.tool.detector.report.DetectorDirectoryReport; @@ -19,8 +25,11 @@ import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.QUACKPATCH_SUBDIRECTORY_NAME; import static org.junit.jupiter.api.Assertions.*; import com.blackduck.integration.detector.rule.DetectableDefinition; +import com.google.gson.Gson; import com.blackduck.integration.detectable.detectables.gradle.inspection.GradleInspectorDetectable; import com.blackduck.integration.common.util.finder.FileFinder; +import com.blackduck.integration.configuration.config.PropertyConfiguration; +import com.blackduck.integration.configuration.property.types.path.SimplePathResolver; import com.blackduck.integration.detectable.detectable.executable.resolver.GradleResolver; import com.blackduck.integration.detectable.detectable.inspector.GradleInspectorResolver; import com.blackduck.integration.detectable.detectables.gradle.inspection.GradleInspectorExtractor; @@ -69,11 +78,14 @@ void testSaveExtractedDetectorsAndTheirRelevantFilePaths_gradle() throws Excepti reportsField.setAccessible(true); reportsField.set(toolResult, reports); - new DetectorTool(null, null, null, null, null, null, null) - .saveExtractedDetectorsAndTheirRelevantFilePaths(directoryManager, toolResult); + + DetectConfigurationFactory detectConfigurationFactory = DetectConfigurationFactoryTestUtils.factoryOf(Pair.of(DetectProperties.DETECT_QUACK_PATCH_OUTPUT, tempDir.toString())); + + new DetectorTool(null, null, null, null, null, null, null, detectConfigurationFactory) + .saveExtractedDetectorsAndTheirRelevantFilePaths(toolResult); File outputFile = new File( - new File(directoryManager.getScanOutputDirectory(), QUACKPATCH_SUBDIRECTORY_NAME), + new File(detectConfigurationFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT), QUACKPATCH_SUBDIRECTORY_NAME), INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON ); assertTrue(outputFile.exists());