Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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,6 +19,7 @@
@CommandLine.Command(name = "issue",
subcommands = {
FoDIssueListCommand.class,
FoDIssueGetCommand.class,
FoDIssueUpdateCommand.class,
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright 2021-2026 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.fod.issue.cli.cmd;

import com.fasterxml.jackson.databind.JsonNode;
import com.fortify.cli.common.exception.FcliSimpleException;
import com.fortify.cli.common.json.producer.IObjectNodeProducer;
import com.fortify.cli.common.json.producer.ObjectNodeProducerApplyFrom;
import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins;
import com.fortify.cli.common.util.DisableTest;
import com.fortify.cli.common.util.DisableTest.TestType;
import com.fortify.cli.fod._common.cli.mixin.FoDDelimiterMixin;
import com.fortify.cli.fod._common.output.cli.cmd.AbstractFoDOutputCommand;
import com.fortify.cli.fod._common.rest.FoDUrls;
import com.fortify.cli.fod._common.rest.helper.FoDInputTransformer;
import com.fortify.cli.fod.issue.cli.mixin.FoDIssueEmbedMixin;
import com.fortify.cli.fod.issue.cli.mixin.FoDIssueIncludeMixin;
import com.fortify.cli.fod.release.cli.mixin.FoDReleaseByQualifiedNameOrIdResolverMixin;

import kong.unirest.HttpRequest;
import kong.unirest.UnirestInstance;
import lombok.Getter;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Parameters;

@DisableTest(TestType.CMD_DEFAULT_TABLE_OPTIONS_PRESENT)
@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class FoDIssueGetCommand extends AbstractFoDOutputCommand {
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
@Mixin private FoDDelimiterMixin delimiterMixin; // Is automatically injected in resolver mixins
@Mixin private FoDReleaseByQualifiedNameOrIdResolverMixin.RequiredOption releaseResolver;
@Parameters(index = "0", arity = "1", descriptionKey = "fcli.fod.issue.get.id")
private String vulnId;
@Mixin private FoDIssueEmbedMixin embedMixin;
@Mixin private FoDIssueIncludeMixin includeMixin;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, same is true here as what I wrote for SSC. Although (contrary to SSC) the FoD endpoint used by this command does support the query parameters generated FoDIssueIncludeMixin, the user shouldn't need to explicitly pass --include if the given issue id is hidden/removed/... If user passes an id, we should just gt that id regardless of issue state. In other words, we should automatically pass query parameters to find issues in any state.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm my understanding the fix here would be to remove the [--include] option from the [get] command and hardcode includeFixed=true and includeSuppressed=true in [findIssue()] for both ssc and fod, so that any issue can be retrieved by ID regardless of its state, without requiring the user to pass extra flags. Is that what you're expecting?

@rsenden rsenden Jun 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct for FoD; you'd always add the request parameters to have FoD return fixed/suppressed/... issues, such that FoD will return the issue data for the requested id independent of whether it's visible, fixed, suppressed, ...

For SSC, you're using an endpoint to get the issue data for a specific issue id. This endpoint will return that data independent of whether the issue is hidden/removed/suppressed; the endpoint doesn't support the request parameters generated by the SSCIssueIncludeMixin, and doesn't support the qm request parameter as explained in other comments.


@Override
protected IObjectNodeProducer getObjectNodeProducer(UnirestInstance unirest) {
String releaseId = releaseResolver.getReleaseId(unirest);
JsonNode issue = getIssue(unirest, releaseId);
return simpleObjectNodeProducerBuilder(ObjectNodeProducerApplyFrom.SPEC)
.source(issue)
.build();
}

private JsonNode getIssue(UnirestInstance unirest, String releaseId) {
boolean numericId = vulnId!=null && vulnId.chars().allMatch(Character::isDigit);
JsonNode issue = numericId
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated
? getIssueByFilter(unirest, releaseId, "id", vulnId)
: getIssueByFilter(unirest, releaseId, "vulnId", vulnId);
if ( issue==null ) {
issue = numericId
? getIssueByFilter(unirest, releaseId, "vulnId", vulnId)
: getIssueByFilter(unirest, releaseId, "id", vulnId);
}
if ( issue==null ) {
throw new FcliSimpleException(String.format("No issue found for id or vulnId '%s' in the specified release", vulnId));
}
return issue;
}

private JsonNode getIssueByFilter(UnirestInstance unirest, String releaseId, String fieldName, String value) {
HttpRequest<?> request = unirest.get(FoDUrls.VULNERABILITIES)
.routeParam("relId", releaseId)
.queryString("filters", fieldName+":"+value)
.queryString("limit", "1");
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated
JsonNode body = includeMixin.updateRequest(request).asObject(JsonNode.class).getBody();
JsonNode items = FoDInputTransformer.getItems(body);
return items!=null && items.isArray() && !items.isEmpty() ? items.get(0) : null;
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated
}

@Override
public boolean isSingular() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,9 @@ fcli.fod.issue.output.table.header.updateCount = Issues Updated
fcli.fod.issue.output.table.header.skippedCount = Issues Skipped
fcli.fod.issue.output.table.header.errorCount = Errors
fcli.fod.issue.list.usage.header = List vulnerabilities.
fcli.fod.issue.get.usage.header = Get vulnerability details.
fcli.fod.issue.get.usage.description = Get detailed data for a single FoD vulnerability in a given release.
fcli.fod.issue.get.id = Vulnerability id or vulnId.
fcli.fod.issue.list.usage.description = This command allows for listing FoD vulnerability data \
for a given application or release. By default, only visible issues will be returned; the --include option can \
be used to (also) include suppressed or fixed issues. If any such issues are included, the \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
SSCIssueGroupGetCommand.class,
SSCIssueGroupListCommand.class,
SSCIssueCountCommand.class,
SSCIssueGetCommand.class,
SSCIssueListCommand.class,
SSCIssueUpdateCommand.class,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2021-2026 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.json.producer.IObjectNodeProducer;
import com.fortify.cli.common.json.producer.ObjectNodeProducerApplyFrom;
import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins;
import com.fortify.cli.ssc._common.output.cli.cmd.AbstractSSCOutputCommand;
import com.fortify.cli.ssc._common.rest.ssc.SSCUrls;
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.SSCIssueIncludeMixin;

import kong.unirest.HttpRequest;
import kong.unirest.UnirestInstance;
import lombok.Getter;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Parameters;

@Command(name = OutputHelperMixins.Get.CMD_NAME)
public class SSCIssueGetCommand extends AbstractSSCOutputCommand {
@Getter @Mixin private OutputHelperMixins.Get outputHelper;
@Mixin private SSCAppVersionResolverMixin.RequiredOption parentResolver;
@Parameters(index = "0", arity = "1", descriptionKey = "fcli.ssc.issue.get.id")
private String id;
@Mixin private SSCIssueBulkEmbedMixin bulkEmbedMixin;
@Mixin private SSCIssueIncludeMixin includeMixin;
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated

@Override
protected IObjectNodeProducer getObjectNodeProducer(UnirestInstance unirest) {
String appVersionId = parentResolver.getAppVersionId(unirest);
return requestObjectNodeProducerBuilder(ObjectNodeProducerApplyFrom.SPEC)
.baseRequest(getBaseRequest(unirest, appVersionId))
.build();
}

private HttpRequest<?> getBaseRequest(UnirestInstance unirest, String appVersionId) {
return unirest.get(SSCUrls.PROJECT_VERSION_ISSUE(appVersionId, id)).queryString("qm", "issues");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please double-check, but I think qm request parameter is not supported by SSC on this endpoint.

Also, we could consider using the issueDetails endpoint instead as that doesn't require application version to be specified (thereby simplifying CLI usage), but output of that endpoint is not the same as what we return on the list command, so users may not expect that inconsistency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the SSC api reference , query parameter is available for this endpoint.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this uses the following endpoint, which doesn't list a qm query parameter:

Image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code still shows qm request parameter, which is not supported on GET /projectVersions/{parentId}/issues/{id} endpoint.

}

@Override
public boolean isSingular() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

public class SSCIssueIncludeMixin implements IHttpRequestUpdater, IRecordTransformer {
@DisableTest(TestType.MULTI_OPT_PLURAL_NAME)
@Option(names = {"--include", "-i"}, split = ",", defaultValue = "visible", descriptionKey = "fcli.ssc.issue.list.includeIssue", paramLabel="<status>")
@Option(names = {"--include", "-i"}, split = ",", defaultValue = "visible", descriptionKey = "fcli.ssc.issue.includeIssue", paramLabel="<status>")
private Set<SSCIssueInclude> includes;

public HttpRequest<?> updateRequest(HttpRequest<?> request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,13 +504,16 @@ fcli.ssc.issue.list.usage.description = This command allows for listing SSC vuln
more immediate output.
fcli.ssc.issue.list.output.table.header.visibilityMarker =
fcli.ssc.issue.list.output.table.header.friority = Priority
fcli.ssc.issue.get.usage.header = Get application version vulnerability details.
Comment thread
jmadhur87 marked this conversation as resolved.
Outdated
fcli.ssc.issue.get.usage.description = Get detailed data for a single SSC vulnerability in a given application version.
fcli.ssc.issue.get.id = Issue id.
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}. \
fcli.ssc.issue.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.list.includeIssue = By default, only visible issues will be returned. This option \
fcli.ssc.issue.includeIssue = By default, only visible issues will be returned. This option \
accepts a comma-separated list to allow (also) removed, suppressed and/or hidden issues to be returned, \
for example `--include visible,removed` (to return both visible and removed issues) or `--include \
removed` (to return only removed issues). Allowed values: ${COMPLETION-CANDIDATES}.
Expand Down Expand Up @@ -704,6 +707,9 @@ fcli.ssc.attribute.definition.output.table.args = id,category,guid,name,type,req
fcli.ssc.aviator.output.table.args = id,application.name,name,artifactId
fcli.ssc.custom-tag.output.table.args = guid,name,valueType
fcli.ssc.issue.count.output.table.args = cleanName,totalCount,auditedCount
fcli.ssc.issue.get.output.table.args = id,visibilityMarker,friority,location,issueName
fcli.ssc.issue.get.output.table.header.visibilityMarker =
fcli.ssc.issue.get.output.table.header.friority = Priority
fcli.ssc.issue.list.output.table.args = id,visibilityMarker,friority,location,issueName
fcli.ssc.issue.filter-set.output.table.args = guid,title,defaultFilterSet
fcli.ssc.issue.group.output.table.args = guid,displayName,entityType
Expand Down
Loading