diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/util/FoDEnums.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/util/FoDEnums.java index 8d044f9483..4754218578 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/util/FoDEnums.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/util/FoDEnums.java @@ -455,4 +455,55 @@ public enum ApiSchemeType { @JsonProperty("http,https") HTTPandHTTPs; } + + public enum AttributeTypes { + All(0), + 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 0: + return "All"; + 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 0: + return All; + case 1: + return Application; + case 2: + return Vulnerability; + case 3: + return Microservice; + case 4: + default: + return Release; + } + } + } + } diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/cli/mixin/FoDAttributeUpdateOptions.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/cli/mixin/FoDAttributeUpdateOptions.java index c9c1025730..77f8b60e20 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/cli/mixin/FoDAttributeUpdateOptions.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/cli/mixin/FoDAttributeUpdateOptions.java @@ -27,6 +27,11 @@ public static abstract class AbstractFoDAppAttributeUpdateMixin { public abstract Map getAttributes(); } + public static class OptionalAttrCreateOption extends AbstractFoDAppAttributeUpdateMixin { + @Option(names = {"--attrs", "--attributes"}, required = false, split=",", paramLabel = PARAM_LABEL, descriptionKey = "fcli.fod.app.create.attr") + @Getter private Map attributes; + } + public static class OptionalAttrOption extends AbstractFoDAppAttributeUpdateMixin { @Option(names = {"--attrs", "--attributes"}, required = false, split=",", paramLabel = PARAM_LABEL, descriptionKey = "fcli.fod.app.update.attr") @Getter private Map attributes; diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/helper/FoDAttributeHelper.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/helper/FoDAttributeHelper.java index b7aeffd51d..e18c7cf379 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/helper/FoDAttributeHelper.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/attr/helper/FoDAttributeHelper.java @@ -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; @@ -27,6 +30,7 @@ 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; @@ -34,6 +38,7 @@ 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) { @@ -52,7 +57,8 @@ public static final FoDAttributeDescriptor getAttributeDescriptor(UnirestInstanc } @SneakyThrows - public static final Map getRequiredAttributesDefaultValues(UnirestInstance unirestInstance) { + public static final Map getRequiredAttributesDefaultValues(UnirestInstance unirestInstance, + FoDEnums.AttributeTypes attrType) { Map reqAttrs = new HashMap<>(); GetRequest request = unirestInstance.get(FoDUrls.ATTRIBUTES) .queryString("filters", "isRequired:true"); @@ -63,8 +69,8 @@ public static final Map getRequiredAttributesDefaultValues(Unire Iterator 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() && (attrType.getValue() == 0 || currentLookup.getAttributeTypeId() == attrType.getValue())) { switch (currentLookup.getAttributeDataType()) { case "Text": reqAttrs.put(currentLookup.getName(), "autofilled by fcli"); @@ -88,7 +94,7 @@ public static final Map getRequiredAttributesDefaultValues(Unire return reqAttrs; } - public static JsonNode mergeAttributesNode(UnirestInstance unirest, + public static JsonNode mergeAttributesNode(UnirestInstance unirest, FoDEnums.AttributeTypes attrType, ArrayList current, Map updates) { ArrayNode attrArray = objectMapper.createArrayNode(); @@ -111,7 +117,7 @@ public static JsonNode mergeAttributesNode(UnirestInstance unirest, return attrArray; } - public static JsonNode getAttributesNode(ArrayList attributes) { + public static JsonNode getAttributesNode(FoDEnums.AttributeTypes attrType, ArrayList attributes) { ArrayNode attrArray = objectMapper.createArrayNode(); if (attributes == null || attributes.isEmpty()) return attrArray; for (FoDAttributeDescriptor attr : attributes) { @@ -123,11 +129,12 @@ public static JsonNode getAttributesNode(ArrayList attri return attrArray; } - public static JsonNode getAttributesNode(UnirestInstance unirest, Map attributesMap, boolean autoReqdAttributes) { + public static JsonNode getAttributesNode(UnirestInstance unirest, FoDEnums.AttributeTypes attrType, + Map attributesMap, boolean autoReqdAttributes) { Map 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); @@ -136,9 +143,15 @@ public static JsonNode getAttributesNode(UnirestInstance unirest, Map 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 (attrType.getValue() == 0 || 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; } diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppCreateCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppCreateCommand.java index 2d3d5ec038..0a3247cc86 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppCreateCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppCreateCommand.java @@ -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; @@ -75,7 +76,7 @@ public class FoDAppCreateCommand extends AbstractFoDJsonNodeOutputCommand implem @Mixin protected FoDCriticalityTypeOptions.RequiredOption criticalityType; @Mixin - protected FoDAttributeUpdateOptions.OptionalAttrOption appAttrs; + protected FoDAttributeUpdateOptions.OptionalAttrCreateOption appAttrs; @Mixin protected FoDSdlcStatusTypeOptions.RequiredOption sdlcStatus; @@ -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.All, + appAttrs.getAttributes(), autoRequiredAttrs)) .userGroupIds(FoDUserGroupHelper.getUserGroupsNode(unirest, userGroups)).build(); return FoDAppHelper.createApp(unirest, appCreateRequest).asJsonNode(); diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppUpdateCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppUpdateCommand.java index 8bf3064f97..816f75f49b 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppUpdateCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/app/cli/cmd/FoDAppUpdateCommand.java @@ -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; @@ -68,9 +69,10 @@ public JsonNode getJsonNode(UnirestInstance unirest) { Map 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); diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCreateCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCreateCommand.java index c70650680c..74c1c6bc98 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCreateCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCreateCommand.java @@ -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; @@ -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())); } diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseUpdateCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseUpdateCommand.java index 8f427de022..f973e956ef 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseUpdateCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseUpdateCommand.java @@ -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; @@ -68,9 +69,10 @@ public JsonNode getJsonNode(UnirestInstance unirest) { Map 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()) diff --git a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/setup-release.yaml b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/setup-release.yaml index 697a3ef5d2..0fe47b4a6c 100644 --- a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/setup-release.yaml +++ b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/setup-release.yaml @@ -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 @@ -49,32 +49,64 @@ 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: attributes + required: false + cliAliases: attrs + description: "Optional comma-separated list of attributes to set on the application and/or release" steps: - progress: "Creating FoD application release if non-existing (profile: ${parameters.profile})" + - if: ${parameters['attributes']!=null} + set: + - name: customAttrArgs + value: --attrs "${parameters['attributes']}" + - if: ${parameters['attributes']==null} + set: + - name: customAttrArgs + 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")} ${customAttrArgs} # 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} - 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. diff --git a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/i18n/FoDMessages.properties b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/i18n/FoDMessages.properties index 45272ca3e4..2ec1371237 100644 --- a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/i18n/FoDMessages.properties +++ b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/i18n/FoDMessages.properties @@ -299,9 +299,13 @@ fcli.fod.app.create.owner = The owner of the application/release who will receiv fcli.fod.app.create.groups = User group ids or names to give access of the application to. fcli.fod.app.create.criticality = The business criticality of the application. Valid values: ${COMPLETION-CANDIDATES}. fcli.fod.app.create.status = The SDLC lifecycle status of the release. Valid values: ${COMPLETION-CANDIDATES} -fcli.fod.app.create.attr = Attribute id or name and its value to set on the application. For User attributes only userId is currently supported. +fcli.fod.app.create.attr = Attribute id or name and its value to set. You can specify application and release attributes. \ + Please note for user attributes only the userId is currently supported. fcli.fod.app.create.skip-if-exists = Check to see if application already exists before creating. -fcli.fod.app.create.auto-required-attrs = Automatically set a default value for required attributes. +fcli.fod.app.create.auto-required-attrs = Automatically set a default value for required attributes. \ + Please note for Picklist and User attributes this will set the value to the first item in the list, \ + for Boolean attributes it will set the value to 'false' and for Text attributes it will fill \ + with 'autofilled by fcli'. fcli.fod.app.create.microservice = The name of the first microservice to create for the application. fcli.fod.app.create.missing-microservice = Missing option: if 'Microservice' type is specified then the 'microservice' option needs to be specified. fcli.fod.app.delete.usage.header = Delete an application. @@ -319,7 +323,8 @@ fcli.fod.app.update.name = The updated name for the application. fcli.fod.app.update.description = The updated description for the application. fcli.fod.app.update.notify = Email address of user(s) to send notifications to (Please note, any existing entries will be replaced). fcli.fod.app.update.criticality = The business criticality of the application. -fcli.fod.app.update.attr = Attribute id or name and its value to set on the application. For User attributes only userId is currently supported. +fcli.fod.app.update.attr = Attribute id or name and its value to set. \ + Please note for user attributes only the userId is currently supported. fcli.fod.app.list-scans.usage.header = List scans for a given application. # fcli fod microservice @@ -361,7 +366,8 @@ fcli.fod.release.create.description = Description of the release. fcli.fod.release.create.copy-from = The id or name of a release to copy existing state from. fcli.fod.release.create.microservice = The id or name of the microservice to create the release on. fcli.fod.release.create.status = SDLC lifecycle status of the release. Valid values: ${COMPLETION-CANDIDATES}. -fcli.fod.release.create.attr = Attribute id or name and its value to set on the release. For User attributes only userId is currently supported. +fcli.fod.release.create.attr = Attribute id or name and its value to set on the release. \ + Please note for user attributes only the userId is currently supported. fcli.fod.release.create.skip-if-exists = Check to see if release already exists before creating. fcli.fod.release.create.auto-required-attrs = Automatically set a default value for required attributes. fcli.fod.release.delete.usage.header = Delete an application release. @@ -376,7 +382,8 @@ fcli.fod.release.update.description = Updated description for the release. fcli.fod.release.update.owner = Updated id or username for the owner of the release. fcli.fod.release.update.microservice = The updated microservice id or name to create the release on. fcli.fod.release.update.status = SDLC lifecycle status of the release. Valid values: ${COMPLETION-CANDIDATES}. -fcli.fod.release.update.attr = Attribute id or name and its value to set on the application. For User attributes only userId is currently supported. +fcli.fod.release.update.attr = Attribute id or name and its value to set on the release. \ + Please note for user attributes only the userId is currently supported. fcli.fod.release.list-assessment-types.usage.header = List assessment types for a given release. fcli.fod.release.list-assessment-types.scan-types = Comma-separated list of scan types for which to list assessment types. Default value: ${DEFAULT-VALUE}. Valid values: ${COMPLETION-CANDIDATES}. fcli.fod.release.list-scans.usage.header = List scans for a given release.