Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
import com.fortify.cli.tool._common.helper.Tool;
import com.fortify.cli.tool._common.helper.ToolInstallationDescriptor;
import com.fortify.cli.tool._common.helper.ToolInstallationOutputDescriptor;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionVersionDescriptor;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionsHelper;

import picocli.CommandLine.Parameters;

/**
* Abstract base class for tool 'get' commands that retrieve information about
* a specific tool version. Similar to AbstractToolListCommand but returns a
* Abstract base class for tool 'get' commands that retrieve information about
* a specific tool version. Similar to AbstractToolListCommand but returns a
* single record instead of a list.
*
* Subclasses must implement:
Expand All @@ -34,50 +35,77 @@
* @author Ruud Senden
*/
public abstract class AbstractToolGetCommand extends AbstractOutputCommand implements IJsonNodeSupplier {

@Parameters(index = "0", descriptionKey = "fcli.tool.get.version")
private String requestedVersion;

@Override
public final JsonNode getJsonNode() {
var toolName = getTool().getToolName();
var toolDefinition = ToolDefinitionsHelper.getToolDefinitionRootDescriptor(toolName);

var tool = getTool();
var toolName = tool.getToolName();
var optDefinition = ToolDefinitionsHelper.tryGetToolDefinitionRootDescriptor(toolName);

if (!tool.requiresToolDefinitions() && optDefinition.isEmpty()) {
return getJsonNodeWithoutDefinitions(toolName);
}

var toolDefinition = optDefinition.orElseGet(
() -> ToolDefinitionsHelper.getToolDefinitionRootDescriptor(toolName));

// Resolve version (handles aliases like 'latest')
var versionDescriptor = toolDefinition.getVersion(requestedVersion);

// Load installation descriptor if tool is installed
var installationDescriptor = ToolInstallationDescriptor.load(toolName, versionDescriptor);

// Check if this is the default (last installed) version
var lastInstalledDescriptor = ToolInstallationDescriptor.loadLastModified(toolName);
boolean isDefault = isDefaultVersion(installationDescriptor, lastInstalledDescriptor);

// Create output descriptor
var outputDescriptor = new ToolInstallationOutputDescriptor(
toolName,
versionDescriptor,
installationDescriptor,
"",
isDefault
);

toolName,
versionDescriptor,
installationDescriptor,
"",
isDefault);

return JsonHelper.getObjectMapper().valueToTree(outputDescriptor);
}

private JsonNode getJsonNodeWithoutDefinitions(String toolName) {
ToolDefinitionVersionDescriptor versionDescriptor = new ToolDefinitionVersionDescriptor();
versionDescriptor.setVersion(requestedVersion);
versionDescriptor.setStable(true);
versionDescriptor.setAliases(new String[0]);

var installationDescriptor = ToolInstallationDescriptor.load(toolName, versionDescriptor);
var lastInstalledDescriptor = ToolInstallationDescriptor.loadLastModified(toolName);
boolean isDefault = isDefaultVersion(installationDescriptor, lastInstalledDescriptor);

var outputDescriptor = new ToolInstallationOutputDescriptor(
toolName,
versionDescriptor,
installationDescriptor,
"",
isDefault);
return JsonHelper.getObjectMapper().valueToTree(outputDescriptor);
}

@Override
public final boolean isSingular() {
return true;
}

private boolean isDefaultVersion(ToolInstallationDescriptor installationDescriptor, ToolInstallationDescriptor lastInstalledDescriptor) {

private boolean isDefaultVersion(ToolInstallationDescriptor installationDescriptor,
ToolInstallationDescriptor lastInstalledDescriptor) {
if (installationDescriptor == null || lastInstalledDescriptor == null) {
return false;
}
return installationDescriptor.getInstallDir() != null
return installationDescriptor.getInstallDir() != null
&& installationDescriptor.getInstallDir().equals(lastInstalledDescriptor.getInstallDir());
}

/**
* @return Tool enum entry for this tool
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import com.fortify.cli.tool._common.helper.Tool;
import com.fortify.cli.tool._common.helper.ToolInstallationDescriptor;
import com.fortify.cli.tool._common.helper.ToolInstallationsResolver;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionVersionDescriptor;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionsHelper;

import lombok.Getter;
import lombok.SneakyThrows;
Expand Down Expand Up @@ -59,7 +61,7 @@ public abstract class AbstractToolRunCommand extends AbstractRunnableCommand {
private String workDir = System.getProperty("user.dir");
@Parameters(descriptionKey="fcli.tool.run.tool-args")
@Getter private List<String> toolArgs;

@Override
public final Integer call() throws Exception {
validateWorkingDirectory();
Expand All @@ -71,13 +73,13 @@ public final Integer call() throws Exception {
return call(baseCommands.get(0));
} catch ( Exception e ) {
if ( baseCommands.size()==1) { throw e; } // No more base commands
LOG.debug("Command execution failed ({}): {}; trying fallback command",
LOG.debug("Command execution failed ({}): {}; trying fallback command",
e.getClass().getSimpleName(), e.getMessage());
baseCommands.remove(0);
}
}
}

private void validateWorkingDirectory() {
File workDirFile = new File(workDir);
if (!workDirFile.exists()) {
Expand All @@ -91,7 +93,7 @@ private void validateWorkingDirectory() {
));
}
}

private final Integer call(List<String> baseCmd) throws Exception {
if ( baseCmd==null ) { throw new FcliBugException("Base command to execute may not be null"); }
var fullCmd = Stream.of(baseCmd, getToolArgs())
Expand All @@ -102,7 +104,7 @@ private final Integer call(List<String> baseCmd) throws Exception {
var pb = new ProcessBuilder()
.command(fullCmd)
.directory(new File(workDir))
// .inheritIO();
// .inheritIO();
// Can't use inheritIO as this as it may inherit original stdout/stderr, rather than
// those created by OutputHelper.OutputType (for example through FcliCommandExecutor).
// Instead, we use pipes and manually copy the output to current System.out/System.err.
Expand All @@ -125,7 +127,7 @@ private final Integer call(List<String> baseCmd) throws Exception {
}
return process.exitValue();
}

private static void inheritIO(final InputStream src, final PrintStream dest) {
new Thread(new Runnable() {
@SneakyThrows
Expand All @@ -136,7 +138,8 @@ public void run() {
}

private final ToolInstallationDescriptor getToolInstallationDescriptor() {
var installations = ToolInstallationsResolver.resolve(getTool());
var tool = getTool();
var installations = ToolInstallationsResolver.resolve(tool);
var toolName = installations.tool().getToolName();
if (StringUtils.isBlank(versionToRun)) {
return checkNotNull(
Expand All @@ -145,6 +148,22 @@ private final ToolInstallationDescriptor getToolInstallationDescriptor() {
.orElse(null),
"No tool installations detected");
}

// SCA: allow run without sca.yaml
if (!tool.requiresToolDefinitions()
&& ToolDefinitionsHelper.tryGetToolDefinitionRootDescriptor(toolName).isEmpty()) {
var descriptor = installations.findByVersion(versionToRun)
.map(ToolInstallationsResolver.ToolInstallationRecord::installationDescriptor)
.orElseGet(() -> {
ToolDefinitionVersionDescriptor vd = new ToolDefinitionVersionDescriptor();
vd.setVersion(versionToRun);
vd.setStable(true);
vd.setAliases(new String[0]);
return ToolInstallationDescriptor.load(toolName, vd);
});
return checkNotNull(descriptor, "No tool installation detected for version " + versionToRun);
}

var versionDescriptor = installations.definition().getVersion(versionToRun);
var descriptor = installations.findByVersion(versionDescriptor.getVersion())
.map(ToolInstallationsResolver.ToolInstallationRecord::installationDescriptor)
Expand All @@ -158,7 +177,7 @@ private ToolInstallationDescriptor checkNotNull(ToolInstallationDescriptor descr
}
return descriptor;
}

protected abstract Tool getTool();
protected List<List<String>> getBaseCommands(ToolInstallationDescriptor descriptor) {
return List.of(getBaseCommand(descriptor));
Expand All @@ -167,5 +186,5 @@ protected List<String> getBaseCommand(ToolInstallationDescriptor descriptor) {
return null;
}
protected void updateProcessBuilder(ProcessBuilder pb) {};

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public enum Tool {
FOD_UPLOADER(new ToolHelperFoDUploader(), "fod-uploader"),
BUGTRACKER_UTILITY(new ToolHelperBugTrackerUtility(), "bugtracker-utility", "fbtu"),
VULN_EXPORTER(new ToolHelperVulnExporter(), "vuln-exporter", "fve"),
DEBRICKED_CLI(new ToolHelperDebrickedCli(), "debricked-cli", "dcli");
DEBRICKED_CLI(new ToolHelperDebrickedCli(), "debricked-cli", "dcli"),
SOURCE_ANALYZER(new ToolHelperSourceAnalyzer(), "sourceanalyzer");

private static final Map<String, Tool> TOOL_NAME_MAP = new HashMap<>();
private static final Map<String, Tool> TOOL_ALIAS_MAP = new HashMap<>();
Expand Down Expand Up @@ -101,7 +102,15 @@ public String getDefaultBinaryName() {
public String getDefaultEnvPrefix() {
return toolHelper.getDefaultEnvPrefix();
}


/**
* Determine if this tool requires tool definitions (e.g., for configuration).
* @return true if tool definitions are required, false otherwise
*/
public boolean requiresToolDefinitions() {
return toolHelper.requiresToolDefinitions();
}

/**
* Interface defining tool-specific helper methods.
* Each tool implementation provides its own concrete helper class.
Expand All @@ -113,6 +122,10 @@ public interface IToolHelper {
default String getDefaultEnvPrefix() {
return getToolName().toUpperCase().replace('-', '_');
}

default boolean requiresToolDefinitions() {
return true;
}
}

/**
Expand Down Expand Up @@ -231,4 +244,31 @@ public String getDefaultEnvPrefix() {
return "DEBRICKED";
}
}

/**
* Helper implementation for sourceanalyzer tool.
*/
private static final class ToolHelperSourceAnalyzer implements IToolHelper {
private static final String TOOL_NAME = "sourceanalyzer";

@Override
public String getToolName() {
return TOOL_NAME;
}

@Override
public String getDefaultBinaryName() {
return PlatformHelper.isWindows() ? "sourceanalyzer.exe" : "sourceanalyzer";
}

@Override
public String getDefaultEnvPrefix() {
return "SOURCEANALYZER";
}

@Override
public boolean requiresToolDefinitions() {
return false;
}
}
}
Loading
Loading