diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueCommands.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueCommands.java index ef5d2dc086..ef6bc63dca 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueCommands.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueCommands.java @@ -32,6 +32,7 @@ SSCIssueGroupGetCommand.class, SSCIssueGroupListCommand.class, SSCIssueCountCommand.class, + SSCIssueListCommand.class, } ) public class SSCIssueCommands extends AbstractContainerCommand { diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueListCommand.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueListCommand.java new file mode 100644 index 0000000000..adb5371e5f --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/cmd/SSCIssueListCommand.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2021, 2023 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + *******************************************************************************/ +package com.fortify.cli.ssc.issue.cli.cmd; + +import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins; +import com.fortify.cli.common.rest.query.IServerSideQueryParamGeneratorSupplier; +import com.fortify.cli.common.rest.query.IServerSideQueryParamValueGenerator; +import com.fortify.cli.ssc._common.output.cli.cmd.AbstractSSCBaseRequestOutputCommand; +import com.fortify.cli.ssc._common.rest.query.SSCQParamGenerator; +import com.fortify.cli.ssc._common.rest.query.SSCQParamValueGenerators; +import com.fortify.cli.ssc._common.rest.query.cli.mixin.SSCQParamMixin; +import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionResolverMixin; +import com.fortify.cli.ssc.issue.cli.mixin.SSCIssueBulkEmbedMixin; +import com.fortify.cli.ssc.issue.cli.mixin.SSCIssueFilterSetResolverMixin; +import com.fortify.cli.ssc.issue.helper.SSCIssueFilterHelper; +import com.fortify.cli.ssc.issue.helper.SSCIssueFilterSetDescriptor; + +import kong.unirest.GetRequest; +import kong.unirest.HttpRequest; +import kong.unirest.UnirestInstance; +import lombok.Getter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Mixin; +import picocli.CommandLine.Option; + +@Command(name = OutputHelperMixins.List.CMD_NAME) +public class SSCIssueListCommand extends AbstractSSCBaseRequestOutputCommand implements IServerSideQueryParamGeneratorSupplier { + @Getter @Mixin private OutputHelperMixins.List outputHelper; + @Mixin private SSCAppVersionResolverMixin.RequiredOption parentResolver; + @Mixin private SSCIssueFilterSetResolverMixin.FilterSetOption filterSetResolver; + @Mixin private SSCQParamMixin qParamMixin; + @Option(names="--filter", required=false) private String filter; + + // For some reason, SSC q param doesn't use same property names as returned by SSC, + // so we list the proper mappings below. TODO Any other useful server-side queries? + @Getter private IServerSideQueryParamValueGenerator serverSideQueryParamGenerator = new SSCQParamGenerator() + .add("issueName", "category", SSCQParamValueGenerators::wrapInQuotes) + .add("fullFileName", "file", SSCQParamValueGenerators::wrapInQuotes); + @Mixin private SSCIssueBulkEmbedMixin bulkEmbedMixin; + + @Override + public HttpRequest getBaseRequest(UnirestInstance unirest) { + String appVersionId = parentResolver.getAppVersionId(unirest); + SSCIssueFilterSetDescriptor filterSetDescriptor = filterSetResolver.getFilterSetDescriptor(unirest, appVersionId); + GetRequest request = unirest.get("/api/v1/projectVersions/{id}/issues?limit=100&qm=issues") + .routeParam("id", appVersionId); + if ( filterSetDescriptor!=null ) { + request.queryString("filterset", filterSetDescriptor.getGuid()); + } + if ( filter!=null ) { + request.queryString("filter", new SSCIssueFilterHelper(unirest, appVersionId).getFilter(filter)); + } + return request; + } + + @Override + public boolean isSingular() { + return false; + } +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueBulkEmbedMixin.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueBulkEmbedMixin.java new file mode 100644 index 0000000000..ded6677bf8 --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueBulkEmbedMixin.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2021, 2023 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + *******************************************************************************/ +package com.fortify.cli.ssc.issue.cli.mixin; + +import com.fortify.cli.common.util.DisableTest; +import com.fortify.cli.common.util.DisableTest.TestType; +import com.fortify.cli.ssc._common.output.cli.mixin.AbstractSSCBulkEmbedMixin; +import com.fortify.cli.ssc.issue.helper.SSCIssueEmbedderSupplier; + +import lombok.Getter; +import picocli.CommandLine.Option; + +public class SSCIssueBulkEmbedMixin extends AbstractSSCBulkEmbedMixin { + @DisableTest(TestType.MULTI_OPT_PLURAL_NAME) + @Option(names = "--embed", required = false, split = ",", descriptionKey = "fcli.ssc.issue.embed" ) + @Getter private SSCIssueEmbedderSupplier[] embedSuppliers; +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueEmbedderSupplier.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueEmbedderSupplier.java new file mode 100644 index 0000000000..4d10104ee2 --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueEmbedderSupplier.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright 2021, 2023 Open Text. + * + * The only warranties for products and services of Open Text + * and its affiliates and licensors ("Open Text") are as may + * be set forth in the express warranty statements accompanying + * such products and services. Nothing herein should be construed + * as constituting an additional warranty. Open Text shall not be + * liable for technical or editorial errors or omissions contained + * herein. The information contained herein is subject to change + * without notice. + *******************************************************************************/ +package com.fortify.cli.ssc.issue.helper; + +import java.util.function.Supplier; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fortify.cli.ssc._common.rest.bulk.ISSCEntityEmbedder; +import com.fortify.cli.ssc._common.rest.bulk.ISSCEntityEmbedderSupplier; +import com.fortify.cli.ssc._common.rest.bulk.SSCBulkRequestBuilder; +import com.fortify.cli.ssc._common.rest.helper.SSCInputTransformer; + +import kong.unirest.HttpRequest; +import kong.unirest.UnirestInstance; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum SSCIssueEmbedderSupplier implements ISSCEntityEmbedderSupplier { + details(SSCIssueDetailsEmbedder::new), + comments(SSCIssueCommentsEmbedder::new), + auditHistory(SSCIssueAuditHistoryEmbedder::new), + ; + + private final Supplier supplier; + + public ISSCEntityEmbedder createEntityEmbedder() { + return supplier.get(); + } + + private static abstract class AbstractSSCIssueEmbedder implements ISSCEntityEmbedder { + @Override + public void addEmbedRequests(SSCBulkRequestBuilder builder, UnirestInstance unirest, JsonNode record) { + var id = record.get("id").asText(); + builder.request(getBaseRequest(unirest) + .routeParam("id", id) + .queryString("limit", "-1"), + response->process((ObjectNode)record, SSCInputTransformer.getDataOrSelf(response))); + } + + protected abstract HttpRequest getBaseRequest(UnirestInstance unirest); + protected abstract void process(ObjectNode record, JsonNode response); + } + + private static final class SSCIssueDetailsEmbedder extends AbstractSSCIssueEmbedder { + @Override + protected HttpRequest getBaseRequest(UnirestInstance unirest) { + return unirest.get("/api/v1/issueDetails/{id}"); + } + @Override + protected void process(ObjectNode record, JsonNode response) { + record.set("details", response); + } + } + + private static final class SSCIssueAuditHistoryEmbedder extends AbstractSSCIssueEmbedder { + @Override + protected HttpRequest getBaseRequest(UnirestInstance unirest) { + return unirest.get("/api/v1/issues/{id}/auditHistory?limit=-1"); + } + @Override + protected void process(ObjectNode record, JsonNode response) { + record.set("auditHistory", response); + } + } + + private static final class SSCIssueCommentsEmbedder extends AbstractSSCIssueEmbedder { + @Override + protected HttpRequest getBaseRequest(UnirestInstance unirest) { + return unirest.get("/api/v1/issues/{id}/comments?limit=-1"); + } + @Override + protected void process(ObjectNode record, JsonNode response) { + record.set("comments", response); + } + } +} diff --git a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties index a625bb353f..166f7eb240 100644 --- a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties +++ b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties @@ -376,6 +376,13 @@ fcli.ssc.issue.count.by = Vulnerability grouping type. See 'fcli ssc issue list- allowed values. Default value: ${DEFAULT-VALUE}. fcli.ssc.issue.count.filter = Filter issue counts using the given (friendly or technical) filter. \ See 'fcli ssc issue list-filters' for allowed values. +fcli.ssc.issue.list.usage.header = List application version vulnerabilities. +fcli.ssc.issue.list.filter = Filter issues using the given (friendly or technical) filter. \ + See 'fcli ssc issue list-filters' for allowed values. +fcli.ssc.issue.list.embed = Embed extra application version data. Allowed values: ${COMPLETION-CANDIDATES}. \ + Using the --output option, this extra data can be included in the output. Using the --query option, \ + this extra data can be queried upon. To get an understanding of the structure and contents of the \ + embedded data, use the --output json or --output yaml options. fcli.ssc.issue.get-filter.usage.header = Get issue filter details. fcli.ssc.issue.filter = Technical or friendly filter as returned by the 'fcli ssc issue list-filters' command. fcli.ssc.issue.list-filters.usage.header = List application version issue filters. @@ -522,6 +529,7 @@ fcli.ssc.artifact.output.table.options = id,scanTypes,lastScanDate,uploadDate,st fcli.ssc.attribute.output.table.options = id,category,guid,name,valueString fcli.ssc.attribute.definition.output.table.options = id,category,guid,name,type,required fcli.ssc.issue.count.output.table.options = cleanName,totalCount,auditedCount +fcli.ssc.issue.list.output.table.options = id,primaryLocation,lineNumber,issueName fcli.ssc.issue.filter-set.output.table.options = guid,title,defaultFilterSet fcli.ssc.issue.group.output.table.options = guid,displayName,entityType fcli.ssc.issue.filter.output.table.options = entityType,friendlyFilter,technicalFilter