Skip to content
/ fcli Public
forked from fortify/fcli

Commit

Permalink
feat: fcli fod release create: Improve handling of application attr…
Browse files Browse the repository at this point in the history
…ibutes in --attrs option (fixes fortify#604)

chore: working `fcli fod action run setup-release`
  • Loading branch information
kadraman committed Sep 20, 2024
1 parent dc79095 commit 3926bd7
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,50 @@ public enum ApiSchemeType {
@JsonProperty("http,https")
HTTPandHTTPs;
}

public enum AttributeTypes {
Application(1),
Vulnerability(2),
Microservice(3),
Release(4);

private final int _val;

AttributeTypes(int val) {
this._val = val;
}

public int getValue() {
return this._val;
}

public String toString() {
switch (this._val) {
case 1:
return "Application";
case 2:
return "Vulnerability";
case 3:
return "Microservice";
case 4:
default:
return "Release";
}
}

public static AttributeTypes fromInt(int val) {
switch (val) {
case 1:
return Application;
case 2:
return Vulnerability;
case 3:
return Microservice;
case 4:
default:
return Release;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -27,13 +30,15 @@
import com.fortify.cli.common.json.JsonHelper;
import com.fortify.cli.fod._common.rest.FoDUrls;
import com.fortify.cli.fod._common.rest.helper.FoDDataHelper;
import com.fortify.cli.fod._common.util.FoDEnums;

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

public class FoDAttributeHelper {
private static final Logger LOG = LoggerFactory.getLogger(FoDAttributeHelper.class);
@Getter private static ObjectMapper objectMapper = new ObjectMapper();

public static final FoDAttributeDescriptor getAttributeDescriptor(UnirestInstance unirestInstance, String attrNameOrId, boolean failIfNotFound) {
Expand All @@ -52,7 +57,8 @@ public static final FoDAttributeDescriptor getAttributeDescriptor(UnirestInstanc
}

@SneakyThrows
public static final Map<String, String> getRequiredAttributesDefaultValues(UnirestInstance unirestInstance) {
public static final Map<String, String> getRequiredAttributesDefaultValues(UnirestInstance unirestInstance,
FoDEnums.AttributeTypes attrType) {
Map<String, String> reqAttrs = new HashMap<>();
GetRequest request = unirestInstance.get(FoDUrls.ATTRIBUTES)
.queryString("filters", "isRequired:true");
Expand All @@ -63,8 +69,8 @@ public static final Map<String, String> getRequiredAttributesDefaultValues(Unire
Iterator<FoDAttributeDescriptor> lookupIterator = lookupList.iterator();
while (lookupIterator.hasNext()) {
FoDAttributeDescriptor currentLookup = lookupIterator.next();
// currentLookup.getAttributeTypeId() == 1 is "Application", 4 is "Release" - filter above does not support querying on this yet!
if (currentLookup.getIsRequired() && (currentLookup.getAttributeTypeId() == 1 || currentLookup.getAttributeTypeId() == 4)) {
// currentLookup.getAttributeTypeId() == 1 if "Application", 4 if "Release" - filter above does not support querying on this yet!
if (currentLookup.getIsRequired() && (currentLookup.getAttributeTypeId() == attrType.getValue())) {
switch (currentLookup.getAttributeDataType()) {
case "Text":
reqAttrs.put(currentLookup.getName(), "autofilled by fcli");
Expand All @@ -88,7 +94,7 @@ public static final Map<String, String> getRequiredAttributesDefaultValues(Unire
return reqAttrs;
}

public static JsonNode mergeAttributesNode(UnirestInstance unirest,
public static JsonNode mergeAttributesNode(UnirestInstance unirest, FoDEnums.AttributeTypes attrType,
ArrayList<FoDAttributeDescriptor> current,
Map<String, String> updates) {
ArrayNode attrArray = objectMapper.createArrayNode();
Expand All @@ -111,7 +117,7 @@ public static JsonNode mergeAttributesNode(UnirestInstance unirest,
return attrArray;
}

public static JsonNode getAttributesNode(ArrayList<FoDAttributeDescriptor> attributes) {
public static JsonNode getAttributesNode(FoDEnums.AttributeTypes attrType, ArrayList<FoDAttributeDescriptor> attributes) {
ArrayNode attrArray = objectMapper.createArrayNode();
if (attributes == null || attributes.isEmpty()) return attrArray;
for (FoDAttributeDescriptor attr : attributes) {
Expand All @@ -123,11 +129,12 @@ public static JsonNode getAttributesNode(ArrayList<FoDAttributeDescriptor> attri
return attrArray;
}

public static JsonNode getAttributesNode(UnirestInstance unirest, Map<String, String> attributesMap, boolean autoReqdAttributes) {
public static JsonNode getAttributesNode(UnirestInstance unirest, FoDEnums.AttributeTypes attrType,
Map<String, String> attributesMap, boolean autoReqdAttributes) {
Map<String, String> combinedAttributesMap = new LinkedHashMap<>();
if (autoReqdAttributes) {
// find any required attributes
combinedAttributesMap.putAll(getRequiredAttributesDefaultValues(unirest));
combinedAttributesMap.putAll(getRequiredAttributesDefaultValues(unirest, attrType));
}
if ( attributesMap!=null && !attributesMap.isEmpty() ) {
combinedAttributesMap.putAll(attributesMap);
Expand All @@ -136,9 +143,15 @@ public static JsonNode getAttributesNode(UnirestInstance unirest, Map<String, St
for (Map.Entry<String, String> attr : combinedAttributesMap.entrySet()) {
ObjectNode attrObj = getObjectMapper().createObjectNode();
FoDAttributeDescriptor attributeDescriptor = FoDAttributeHelper.getAttributeDescriptor(unirest, attr.getKey(), true);
attrObj.put("id", attributeDescriptor.getId());
attrObj.put("value", attr.getValue());
attrArray.add(attrObj);
// filter out any attributes that aren't valid for the entity we are working on, e.g. Application or Release
if (attributeDescriptor.getAttributeTypeId() == attrType.getValue()) {
attrObj.put("id", attributeDescriptor.getId());
attrObj.put("value", attr.getValue());
attrArray.add(attrObj);
} else {
LOG.debug("Skipping attribute '"+attributeDescriptor.getName()+"' as it is not a "+attrType.toString()+" attribute");

}
}
return attrArray;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.fortify.cli.common.util.DisableTest;
import com.fortify.cli.common.util.StringUtils;
import com.fortify.cli.fod._common.output.cli.cmd.AbstractFoDJsonNodeOutputCommand;
import com.fortify.cli.fod._common.util.FoDEnums;
import com.fortify.cli.fod.access_control.helper.FoDUserGroupHelper;
import com.fortify.cli.fod.access_control.helper.FoDUserHelper;
import com.fortify.cli.fod.app.attr.cli.mixin.FoDAttributeUpdateOptions;
Expand Down Expand Up @@ -100,7 +101,8 @@ public JsonNode getJsonNode(UnirestInstance unirest) {
.applicationType(appType.getAppType().getFoDValue())
.hasMicroservices(appType.getAppType().isMicroservice())
.microservices(FoDAppHelper.getMicroservicesNode(microservices))
.attributes(FoDAttributeHelper.getAttributesNode(unirest, appAttrs.getAttributes(), autoRequiredAttrs))
.attributes(FoDAttributeHelper.getAttributesNode(unirest, FoDEnums.AttributeTypes.Application,
appAttrs.getAttributes(), autoRequiredAttrs))
.userGroupIds(FoDUserGroupHelper.getUserGroupsNode(unirest, userGroups)).build();

return FoDAppHelper.createApp(unirest, appCreateRequest).asJsonNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.fortify.cli.common.util.DisableTest;
import com.fortify.cli.common.util.StringUtils;
import com.fortify.cli.fod._common.output.cli.cmd.AbstractFoDJsonNodeOutputCommand;
import com.fortify.cli.fod._common.util.FoDEnums;
import com.fortify.cli.fod.app.attr.cli.mixin.FoDAttributeUpdateOptions;
import com.fortify.cli.fod.app.attr.helper.FoDAttributeDescriptor;
import com.fortify.cli.fod.app.attr.helper.FoDAttributeHelper;
Expand Down Expand Up @@ -68,9 +69,10 @@ public JsonNode getJsonNode(UnirestInstance unirest) {
Map<String, String> attributeUpdates = appAttrsUpdate.getAttributes();
JsonNode jsonAttrs = objectMapper.createArrayNode();
if (attributeUpdates != null && !attributeUpdates.isEmpty()) {
jsonAttrs = FoDAttributeHelper.mergeAttributesNode(unirest, appAttrsCurrent, attributeUpdates);
jsonAttrs = FoDAttributeHelper.mergeAttributesNode(unirest, FoDEnums.AttributeTypes.Application, appAttrsCurrent,
attributeUpdates);
} else {
jsonAttrs = FoDAttributeHelper.getAttributesNode(appAttrsCurrent);
jsonAttrs = FoDAttributeHelper.getAttributesNode(FoDEnums.AttributeTypes.Application, appAttrsCurrent);
}
String appEmailListNew = FoDAppHelper.getEmailList(notificationsUpdate);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.fortify.cli.common.util.StringUtils;
import com.fortify.cli.fod._common.cli.mixin.FoDDelimiterMixin;
import com.fortify.cli.fod._common.output.cli.cmd.AbstractFoDJsonNodeOutputCommand;
import com.fortify.cli.fod._common.util.FoDEnums;
import com.fortify.cli.fod.app.attr.cli.mixin.FoDAttributeUpdateOptions;
import com.fortify.cli.fod.app.attr.helper.FoDAttributeHelper;
import com.fortify.cli.fod.app.cli.mixin.FoDSdlcStatusTypeOptions;
Expand Down Expand Up @@ -79,7 +80,8 @@ public JsonNode getJsonNode(UnirestInstance unirest) {
.releaseName(simpleReleaseName)
.releaseDescription(description)
.sdlcStatusType(sdlcStatus.getSdlcStatusType().name())
.attributes(FoDAttributeHelper.getAttributesNode(unirest, relAttrs.getAttributes(), autoRequiredAttrs));
.attributes(FoDAttributeHelper.getAttributesNode(unirest, FoDEnums.AttributeTypes.Release,
relAttrs.getAttributes(), autoRequiredAttrs));
if ( microserviceDescriptor!=null ) {
requestBuilder = requestBuilder.microserviceId(Integer.valueOf(microserviceDescriptor.getMicroserviceId()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.fortify.cli.common.util.StringUtils;
import com.fortify.cli.fod._common.cli.mixin.FoDDelimiterMixin;
import com.fortify.cli.fod._common.output.cli.cmd.AbstractFoDJsonNodeOutputCommand;
import com.fortify.cli.fod._common.util.FoDEnums;
import com.fortify.cli.fod.app.attr.cli.mixin.FoDAttributeUpdateOptions;
import com.fortify.cli.fod.app.attr.helper.FoDAttributeDescriptor;
import com.fortify.cli.fod.app.attr.helper.FoDAttributeHelper;
Expand Down Expand Up @@ -68,9 +69,10 @@ public JsonNode getJsonNode(UnirestInstance unirest) {
Map<String, String> attributeUpdates = appAttrsUpdate.getAttributes();
JsonNode jsonAttrs = objectMapper.createArrayNode();
if (attributeUpdates != null && !attributeUpdates.isEmpty()) {
jsonAttrs = FoDAttributeHelper.mergeAttributesNode(unirest, releaseAttrsCurrent, attributeUpdates);
jsonAttrs = FoDAttributeHelper.mergeAttributesNode(unirest, FoDEnums.AttributeTypes.Release,
releaseAttrsCurrent, attributeUpdates);
} else {
jsonAttrs = FoDAttributeHelper.getAttributesNode(releaseAttrsCurrent);
jsonAttrs = FoDAttributeHelper.getAttributesNode(FoDEnums.AttributeTypes.Release, releaseAttrsCurrent);
}
FoDReleaseUpdateRequest appRelUpdateRequest = FoDReleaseUpdateRequest.builder()
.releaseName(StringUtils.isNotBlank(releaseName) ? getUnqualifiedReleaseName(releaseName, releaseDescriptor) : releaseDescriptor.getReleaseName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ parameters:
cliAliases: t
required: false
type: array
description: "Optional comma-separated list of scan type(s) to set up; for now, only 'sast' is supported"
description: "Optional comma-separated list of scan type(s) to set up; for now, only 'sast' is supported"
- group: rel_create_opts
required: false
name: copy-from
Expand All @@ -49,32 +49,65 @@ parameters:
name: sdlc-status
cliAliases: status
description: "See `fcli fod release create`"

- group: sast_setup_opts
name: assessment-type
required: false
defaultValue: "Static Assessment"
description: "See `fcli fod sast-scan setup`"
- group: sast_setup_opts
required: false
name: use-aviator
description: "See `fcli fod sast-scan setup`"
- group: sast_setup_opts
required: false
name: oss
description: "See `fcli fod sast-scan setup`"
- name: release-attributes
required: false
type: array
cliAliases: rel-attrs
description: "Optional comma-separated list of attributes to set on the release"

steps:
- progress: "Creating FoD application release if non-existing (profile: ${parameters.profile})"
- if: ${parameters['release-attributes']!=null}
set:
- name: relAttrArgs
value: --attrs "${parameters['release-attributes']}"
- if: ${parameters['release-attributes']==null}
append:
- name: relAttrArgs
value: --auto-required-attrs
- if: ${parameters.profile=="default"}
set:
- name: relCreateArgs
value: --skip-if-exists ${#action.copyParametersFromGroup("rel_create_opts")}
value: --skip-if-exists ${#action.copyParametersFromGroup("rel_create_opts")} ${relAttrArgs}
# Custom actions can replace/repeat the above to define custom profiles.
- if: ${relCreateArgs==null}
throw: "Invalid profile: ${parameters.profile}"
- fcli:
- name: createRelease
args: fod release create ${parameters.release} ${relCreateArgs}
args: fod release create "${parameters.release}" ${relCreateArgs} ${relAttrArgs}
- write:
- to: stdout
value: |
Create application release ${parameters.release} (id ${createRelease[0].releaseId}): ${createRelease[0].__action__}
- if: ${parameters["scan-types"].contains("sast")}
steps:
- if: ${parameters.profile=="default"}
set:
- name: sastSetupArgs
value: --skip-if-exists --frequency "Subscription" --audit-preference Automated ${#action.copyParametersFromGroup("sast_setup_opts")}
- progress: "Configuring FoD application release ${parameters.release} for SAST scanning"
- fcli:
- name: setupSast
args: fod sast-scan setup --rel ${parameters.release} --skip-if-exists --assessment-type "Static Assessment" --frequency "Subscription" --audit-preference Automated
args: fod sast-scan setup --rel "${parameters.release}" ${sastSetupArgs}
- write:
- to: stdout
value: |
SAST scan setup status: ${setupSast[0].__action__}
- if: ${parameters["scan-types"].contains("dast")}
steps:
- if: ${parameters.profile=="default"}
# Custom actions can replace the above to define DAST setup.

0 comments on commit 3926bd7

Please sign in to comment.