Skip to content

Commit

Permalink
chore: SSC: Add issue count filters & related commands
Browse files Browse the repository at this point in the history
  • Loading branch information
rsenden committed Oct 19, 2023
1 parent 52d8de2 commit 5d27506
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ public static class GetFilterSet extends DetailsNoQuery {
public static final String CMD_NAME = "get-filterset";
}

@Command(aliases = {"lsf"})
public static class ListFilters extends TableWithQuery {
public static final String CMD_NAME = "list-filters";
}

public static class GetFilter extends DetailsNoQuery {
public static final String CMD_NAME = "get-filter";
}

@Command(aliases = {"lsg"})
public static class ListGroups extends TableWithQuery {
public static final String CMD_NAME = "list-groups";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
subcommands = {
SSCIssueFilterSetGetCommand.class,
SSCIssueFilterSetListCommand.class,
SSCIssueFilterGetCommand.class,
SSCIssueFiltersListCommand.class,
SSCIssueGroupGetCommand.class,
SSCIssueGroupListCommand.class,
SSCIssueCountCommand.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionResolverMixin;
import com.fortify.cli.ssc.issue.cli.mixin.SSCIssueFilterSetResolverMixin;
import com.fortify.cli.ssc.issue.cli.mixin.SSCIssueGroupResolverMixin;
import com.fortify.cli.ssc.issue.helper.SSCIssueFilterHelper;
import com.fortify.cli.ssc.issue.helper.SSCIssueFilterSetDescriptor;
import com.fortify.cli.ssc.issue.helper.SSCIssueGroupDescriptor;

Expand All @@ -27,13 +28,15 @@
import lombok.Getter;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;

@Command(name = SSCOutputHelperMixins.VulnCount.CMD_NAME)
public class SSCIssueCountCommand extends AbstractSSCBaseRequestOutputCommand {
@Getter @Mixin private SSCOutputHelperMixins.VulnCount outputHelper;
@Mixin private SSCAppVersionResolverMixin.RequiredOption parentResolver;
@Mixin private SSCIssueGroupResolverMixin.GroupByOption groupSetResolver;
@Mixin private SSCIssueFilterSetResolverMixin.FilterSetOption filterSetResolver;
@Option(names="--filter", required=false) private String filter;

// TODO Include options for includeRemoved/Hidden/Suppressed?

Expand All @@ -49,6 +52,9 @@ public HttpRequest<?> getBaseRequest(UnirestInstance unirest) {
if ( filterSetDescriptor!=null ) {
request.queryString("filterset", filterSetDescriptor.getGuid());
}
if ( filter!=null ) {
request.queryString("filter", new SSCIssueFilterHelper(unirest, appVersionId).getFilter(filter));
}
return request;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* 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.fasterxml.jackson.databind.JsonNode;
import com.fortify.cli.common.cli.util.CommandGroup;
import com.fortify.cli.common.cli.util.EnvSuffix;
import com.fortify.cli.ssc._common.output.cli.cmd.AbstractSSCJsonNodeOutputCommand;
import com.fortify.cli.ssc._common.output.cli.mixin.SSCOutputHelperMixins;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionResolverMixin;
import com.fortify.cli.ssc.issue.helper.SSCIssueFilterHelper;

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

@Command(name = SSCOutputHelperMixins.GetFilter.CMD_NAME) @CommandGroup("filter")
public class SSCIssueFilterGetCommand extends AbstractSSCJsonNodeOutputCommand {
@Getter @Mixin private SSCOutputHelperMixins.GetFilter outputHelper;
@Mixin private SSCAppVersionResolverMixin.RequiredOption parentResolver;
@EnvSuffix("FILTER") @Parameters(index = "0", arity = "1", descriptionKey = "fcli.ssc.issue.filter")
@Getter private String filter;

@Override
public JsonNode getJsonNode(UnirestInstance unirest) {
return new SSCIssueFilterHelper(unirest, parentResolver.getAppVersionId(unirest)).getFilterNode(filter);
}

@Override
public boolean isSingular() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*******************************************************************************
* 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.fasterxml.jackson.databind.JsonNode;
import com.fortify.cli.common.cli.util.CommandGroup;
import com.fortify.cli.ssc._common.output.cli.cmd.AbstractSSCJsonNodeOutputCommand;
import com.fortify.cli.ssc._common.output.cli.mixin.SSCOutputHelperMixins;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionResolverMixin;
import com.fortify.cli.ssc.issue.helper.SSCIssueFilterHelper;

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

@Command(name = SSCOutputHelperMixins.ListFilters.CMD_NAME) @CommandGroup("filter")
public class SSCIssueFiltersListCommand extends AbstractSSCJsonNodeOutputCommand {
@Getter @Mixin private SSCOutputHelperMixins.ListFilters outputHelper;
@Mixin private SSCAppVersionResolverMixin.RequiredOption parentResolver;

@Override
public JsonNode getJsonNode(UnirestInstance unirest) {
return new SSCIssueFilterHelper(unirest, parentResolver.getAppVersionId(unirest)).getFilterNodes();
}

@Override
public boolean isSingular() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*******************************************************************************
* 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.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fortify.cli.common.json.JsonHelper;
import com.fortify.cli.ssc._common.rest.SSCUrls;

import kong.unirest.UnirestInstance;
import lombok.Getter;

public final class SSCIssueFilterHelper {
private final MultiValueMap<String, String> technicalFiltersByFriendlyFilter = new LinkedMultiValueMap<>();
private final Map<String, JsonNode> filterNodesByTechnicalFilter = new LinkedHashMap<>();
@Getter private final ArrayNode filterNodes = JsonHelper.getObjectMapper().createArrayNode();

public SSCIssueFilterHelper(UnirestInstance unirest, String applicationVersionId) {
JsonNode data = unirest.get(SSCUrls.PROJECT_VERSION_ISSUE_SELECTOR_SET(applicationVersionId))
.asObject(JsonNode.class).getBody()
.get("data")
.get("filterBySet");
data.forEach(this::processEntity);
}

public final String getFilter(String technicalOrFriendlyFilter) {
if ( filterNodesByTechnicalFilter.containsKey(technicalOrFriendlyFilter) ) {
return technicalOrFriendlyFilter;
} else {
var matchingFriendlyFilters = technicalFiltersByFriendlyFilter.get(technicalOrFriendlyFilter);
if ( matchingFriendlyFilters==null || matchingFriendlyFilters.size()==0 ) {
throw new IllegalArgumentException(technicalOrFriendlyFilter+" is not a supported filter");
} else if ( matchingFriendlyFilters.size()>1 ) {
throw new IllegalArgumentException(technicalOrFriendlyFilter+" is ambiguous.\n" +
"please use one of the following filters:\n"+
matchingFriendlyFilters.stream().map(s->s.indent(2)).collect(Collectors.joining("\n")));
} else {
return matchingFriendlyFilters.get(0);
}
}
}

public final JsonNode getFilterNode(String technicalOrFriendlyFilter) {
return filterNodesByTechnicalFilter.get(getFilter(technicalOrFriendlyFilter));
}

private final void processEntity(JsonNode entity) {
processSelectors(entity, entity.get("selectorOptions"));
}

private final void processSelectors(JsonNode entity, JsonNode selectors) {
selectors.forEach(selector->processSelector(entity, selector));
}

private final void processSelector(JsonNode entity, JsonNode selector) {
var newEntity = (ObjectNode)entity.deepCopy();
newEntity.remove("selectorOptions");
newEntity.set("selector", selector);
var filter = JsonHelper.evaluateSpelExpression(newEntity, "entityType+'['+value+']:'+selector.value", String.class);
var friendlyFilter = JsonHelper.evaluateSpelExpression(newEntity, "displayName+':'+selector.displayName?:'NONE'", String.class);
newEntity.put("technicalFilter", filter);
newEntity.put("friendlyFilter", friendlyFilter);
filterNodes.add(newEntity);
filterNodesByTechnicalFilter.put(filter, newEntity);
technicalFiltersByFriendlyFilter.add(friendlyFilter, filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,12 @@ fcli.ssc.event.list.usage.header = List system events.
# fcli ssc issue
fcli.ssc.issue.usage.header = Manage SSC issues (vulnerabilities) and related entities like filter sets and issue groups.
fcli.ssc.issue.count.usage.header = Count application version vulnerabilities by grouping.
fcli.ssc.issue.count.by = Vulnerability grouping type. See 'fcli ssc issue-group list' for \
fcli.ssc.issue.count.by = Vulnerability grouping type. See 'fcli ssc issue list-groups' for \
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.get-filter.usage.header = Get issue filter details.
fcli.ssc.issue.list-filters.usage.header = List application version issue filters.
fcli.ssc.issue.get-filterset.usage.header = Get filter set details.
fcli.ssc.issue.list-filtersets.usage.header = List application version filter sets.
fcli.ssc.issue.filterset.resolver.titleOrId = Filter set title (name) or id.
Expand Down Expand Up @@ -432,6 +436,7 @@ fcli.ssc.event.output.table.options = eventDate,userName,eventType,detailedNote,
fcli.ssc.issue.count.output.table.options = cleanName,totalCount,auditedCount
fcli.ssc.issue.filter-set.output.table.options = guid,title,defaultFilterSet,description
fcli.ssc.issue.group.output.table.options = guid,displayName,entityType,description
fcli.ssc.issue.filter.output.table.options = entityType,friendlyFilter,technicalFilter
fcli.ssc.issue-template.output.table.options = id,name,inUse,defaultTemplate,publishVersion,originalFileName,description
fcli.ssc.job.output.table.options = jobName,jobGroup,jobClass,state,cancellable,priority,createTime,startTime,finishTime
fcli.ssc.performance-indicator.output.table.options = id,name,timestamp,valueString
Expand Down

0 comments on commit 5d27506

Please sign in to comment.