From 7920a40b0d395dc787df7d7ea621402c29545b7c Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:34:23 +0200 Subject: [PATCH 1/6] feat: `fcli sc-sast scan start`: Add support for passing scan arguments through `--sargs` option (resolves #449) (#627) Co-authored-by: Sangamesh Vijaykumar --- .../cmd/SCSastControllerScanStartCommand.java | 71 ++++++++++++++++--- .../cli/mixin/ISCSastScanStartOptions.java | 1 - .../cli/mixin/SCSastScanStartMbsOptions.java | 3 +- .../mixin/SCSastScanStartPackageOptions.java | 1 - .../sc_sast/i18n/SCSastMessages.properties | 5 ++ 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/cmd/SCSastControllerScanStartCommand.java b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/cmd/SCSastControllerScanStartCommand.java index 1dfe2e0fb6..c2198aba98 100644 --- a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/cmd/SCSastControllerScanStartCommand.java +++ b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/cmd/SCSastControllerScanStartCommand.java @@ -16,6 +16,11 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -35,6 +40,7 @@ import kong.unirest.MultipartBody; import kong.unirest.UnirestInstance; import lombok.Getter; +import lombok.RequiredArgsConstructor; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; @@ -50,21 +56,22 @@ public final class SCSastControllerScanStartCommand extends AbstractSCSastContro @Mixin private SCSastSensorPoolResolverMixin.OptionalOption sensorPoolResolver; @Mixin private PublishToAppVersionResolverMixin sscAppVersionResolver; @Option(names = "--ssc-ci-token") private String ciToken; - - // TODO Add options for specifying (custom) rules file(s), filter file(s) and project template - // TODO Add options for pool selection + @Option(names = { "--sargs", "--scan-args" }) + private String scanArguments = ""; @Override public final JsonNode getJsonNode(UnirestInstance unirest) { String sensorVersion = normalizeSensorVersion(optionsProvider.getScanStartOptions().getSensorVersion()); + var scanArgsHelper = ScanArgsHelper.parse(scanArguments); MultipartBody body = unirest.post("/rest/v2/job") .multiPartContent() - .field("zipFile", createZipFile(), "application/zip") + .field("zipFile", createZipFile(scanArgsHelper.getInputFileToZipEntryMap()), "application/zip") .field("username", userName, "text/plain") .field("scaVersion", sensorVersion, "text/plain") .field("clientVersion", sensorVersion, "text/plain") - .field("scaRuntimeArgs", optionsProvider.getScanStartOptions().getScaRuntimeArgs(), "text/plain") - .field("jobType", optionsProvider.getScanStartOptions().getJobType().name(), "text/plain"); + .field("jobType", optionsProvider.getScanStartOptions().getJobType().name(), "text/plain") + .field("scaRuntimeArgs", scanArgsHelper.getScanArgs(), "text/plain"); + body = updateBody(body, "email", email); body = updateBody(body, "buildId", optionsProvider.getScanStartOptions().getBuildId()); body = updateBody(body, "pvId", getAppVersionId()); @@ -72,6 +79,7 @@ public final JsonNode getJsonNode(UnirestInstance unirest) { body = updateBody(body, "uploadToken", getUploadToken()); body = updateBody(body, "dotNetRequired", String.valueOf(optionsProvider.getScanStartOptions().isDotNetRequired())); body = updateBody(body, "dotNetFrameworkRequiredVersion", optionsProvider.getScanStartOptions().getDotNetVersion()); + JsonNode response = body.asObject(JsonNode.class).getBody(); if ( !response.has("token") ) { throw new IllegalStateException("Unexpected response when submitting scan job: "+response); @@ -80,7 +88,7 @@ public final JsonNode getJsonNode(UnirestInstance unirest) { return SCSastControllerScanJobHelper.getScanJobDescriptor(unirest, scanJobToken, StatusEndpointVersion.v1).asJsonNode(); } - @Override + @Override public final String getActionCommandResult() { return "SCAN_REQUESTED"; } @@ -137,14 +145,17 @@ private final MultipartBody updateBody(MultipartBody body, String field, String return StringUtils.isBlank(value) ? body : body.field(field, value, "text/plain"); } - private File createZipFile() { + private File createZipFile(Map extraFiles) { try { File zipFile = File.createTempFile("zip", ".zip"); zipFile.deleteOnExit(); try (FileOutputStream fout = new FileOutputStream(zipFile); ZipOutputStream zout = new ZipOutputStream(fout)) { final String fileName = (optionsProvider.getScanStartOptions().getJobType() == SCSastControllerJobType.TRANSLATION_AND_SCAN_JOB) ? "translation.zip" : "session.mbs"; addFile( zout, fileName, optionsProvider.getScanStartOptions().getPayloadFile()); - // TODO Add rule files, filter files, issue template + + for (var extraFile : extraFiles.entrySet() ) { + addFile(zout, extraFile.getValue(), extraFile.getKey()); + } } return zipFile; } catch (IOException e) { @@ -152,7 +163,7 @@ private File createZipFile() { } } - private void addFile(ZipOutputStream zout, String fileName, File file) throws IOException { + private void addFile(ZipOutputStream zout, String fileName, File file) throws IOException { try ( FileInputStream in = new FileInputStream(file)) { zout.putNextEntry(new ZipEntry(fileName)); byte[] buffer = new byte[1024]; @@ -169,4 +180,42 @@ private static final class PublishToAppVersionResolverMixin extends AbstractSSCA @Getter private String appVersionNameOrId; public final boolean hasValue() { return StringUtils.isNotBlank(appVersionNameOrId); } } -} + + @RequiredArgsConstructor + private static final class ScanArgsHelper { + @Getter private final String scanArgs; + @Getter private final Map inputFileToZipEntryMap; + + public static final ScanArgsHelper parse(String scanArgs) { + List newArgs = new ArrayList<>(); + Map inputFileToZipEntryMap = new LinkedHashMap<>(); + String[] parts = scanArgs.split(" (?=(?:[^\']*\'[^\']*\')*[^\']*$)"); + for ( var part: parts ) { + var inputFileName = getInputFileName(part); + if ( inputFileName==null ) { + newArgs.add(part.replace("'", "\"")); + } else { + var inputFile = new File(inputFileName); + if ( !inputFile.canRead() ) { + throw new IllegalArgumentException("Can't read file "+inputFileName+" as specified in --sargs"); + } + // Re-use existing zip entry name if same file was processed before + var zipEntryFileName = inputFileToZipEntryMap.getOrDefault(inputFile, getZipEntryFileName(inputFileName)); + newArgs.add("\""+zipEntryFileName+"\""); + inputFileToZipEntryMap.put(inputFile, zipEntryFileName); + } + } + return new ScanArgsHelper(String.join(" ", newArgs), inputFileToZipEntryMap); + } + + private static final String getInputFileName(String part) { + var pattern = Pattern.compile("^'?file:'?([^\']*)'?$"); + var matcher = pattern.matcher(part); + return matcher.matches() ? matcher.group(1) : null; + } + + private static final String getZipEntryFileName(String orgFileName) { + return orgFileName.replaceAll("[^A-Za-z0-9.]", "_"); + } + } +} \ No newline at end of file diff --git a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/ISCSastScanStartOptions.java b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/ISCSastScanStartOptions.java index 060e8f5127..2bdd47fd39 100644 --- a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/ISCSastScanStartOptions.java +++ b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/ISCSastScanStartOptions.java @@ -18,7 +18,6 @@ public interface ISCSastScanStartOptions { String getBuildId(); - String getScaRuntimeArgs(); boolean isDotNetRequired(); String getDotNetVersion(); File getPayloadFile(); diff --git a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartMbsOptions.java b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartMbsOptions.java index 5a56359393..1c381f6795 100644 --- a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartMbsOptions.java +++ b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartMbsOptions.java @@ -32,7 +32,6 @@ public class SCSastScanStartMbsOptions implements ISCSastScanStartOptions { @Getter private String buildId; @Getter private final boolean dotNetRequired = false; @Getter private final String dotNetVersion = null; - @Getter private final String scaRuntimeArgs = ""; // TODO Provide options @Getter private SCSastControllerJobType jobType = SCSastControllerJobType.SCAN_JOB; @Option(names = {"-m", "--mbs-file"}, required= true) @@ -40,7 +39,7 @@ public void setMbsFile(File mbsFile) { this.payloadFile = mbsFile; setMbsProperties(mbsFile); } - + private void setMbsProperties(File mbsFile) { try ( FileSystem fs = FileSystems.newFileSystem(mbsFile.toPath()) ) { Path mbsManifest = fs.getPath("MobileBuildSession.manifest"); diff --git a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartPackageOptions.java b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartPackageOptions.java index ceb9314517..19dcf43923 100644 --- a/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartPackageOptions.java +++ b/fcli-core/fcli-sc-sast/src/main/java/com/fortify/cli/sc_sast/scan/cli/mixin/SCSastScanStartPackageOptions.java @@ -33,7 +33,6 @@ public class SCSastScanStartPackageOptions implements ISCSastScanStartOptions { @Getter private final String buildId = null; // TODO ScanCentral Client doesn't allow for specifying build id; should we provide a CLI option for this? @Getter private boolean dotNetRequired; @Getter private String dotNetVersion; - @Getter private final String scaRuntimeArgs = ""; @Getter private SCSastControllerJobType jobType = SCSastControllerJobType.TRANSLATION_AND_SCAN_JOB; @Option(names = {"-p", "--package-file"}, required = true) diff --git a/fcli-core/fcli-sc-sast/src/main/resources/com/fortify/cli/sc_sast/i18n/SCSastMessages.properties b/fcli-core/fcli-sc-sast/src/main/resources/com/fortify/cli/sc_sast/i18n/SCSastMessages.properties index 58b29cf819..1f7ed00324 100644 --- a/fcli-core/fcli-sc-sast/src/main/resources/com/fortify/cli/sc_sast/i18n/SCSastMessages.properties +++ b/fcli-core/fcli-sc-sast/src/main/resources/com/fortify/cli/sc_sast/i18n/SCSastMessages.properties @@ -123,6 +123,11 @@ fcli.sc-sast.scan.start.package-file = Package file to scan. fcli.sc-sast.scan.start.notify = Email address to which to send a scan completion notification. fcli.sc-sast.scan.start.sensor-version = Version of the sensor on which the package should be scanned. Officially, you should select the same sensor version as the version of the ScanCentral Client used to create the package. fcli.sc-sast.scan.start.publish-to = Publish scan results to the given SSC application version once the scan has completed. +fcli.sc-sast.scan.start.sargs = Fortify Static Code Analyzer scan arguments, see ScanCentral SAST documentation for supported \ + scan arguments for your ScanCentral SAST version. Multiple scan arguments must be provided as a single option argument, \ + arguments containing spaces must be embedded in single quotes, and local files must be referenced through the 'file:' prefix. \ + Note that contrary to fcli, scan arguments usually start with a single dash, not double dashes. For example: \ + %n --sargs "-quick -filter 'file:./my filters.txt'" fcli.sc-sast.scan.status.usage.header = Get status for a previously submitted scan request. fcli.sc-sast.scan.wait-for.usage.header = Wait for one or more scans to reach or exit specified scan statuses. fcli.sc-sast.scan.wait-for.usage.description.0 = Although this command offers a lot of options to cover many \ From 0cdde30583e5ba270cf3f605fedc64511164f8b8 Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:52:38 +0200 Subject: [PATCH 2/6] feat: Add `fcli fod release wait-for` command to wait for release(s) to leave suspended state (resolves #624) --- .../release/cli/cmd/FoDReleaseCommands.java | 3 +- .../cli/cmd/FoDReleaseWaitForCommand.java | 48 +++++++++++++++++++ ...leaseByQualifiedNameOrIdResolverMixin.java | 27 +++++++++++ .../cli/fod/i18n/FoDMessages.properties | 21 +++++++- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseWaitForCommand.java diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCommands.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCommands.java index 2f35f2c9b8..823d7db423 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCommands.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseCommands.java @@ -27,7 +27,8 @@ FoDReleaseUpdateCommand.class, FoDReleaseDeleteCommand.class, FoDReleaseAssessmentTypeListCommand.class, - FoDReleaseScanListCommand.class + FoDReleaseScanListCommand.class, + FoDReleaseWaitForCommand.class } ) @DefaultVariablePropertyName("releaseId") diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseWaitForCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseWaitForCommand.java new file mode 100644 index 0000000000..bd85d00adb --- /dev/null +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/cmd/FoDReleaseWaitForCommand.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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.fod.release.cli.cmd; + +import java.util.Set; + +import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins; +import com.fortify.cli.common.rest.cli.cmd.AbstractWaitForCommand; +import com.fortify.cli.common.rest.wait.WaitHelper.WaitHelperBuilder; +import com.fortify.cli.fod._common.cli.mixin.FoDDelimiterMixin; +import com.fortify.cli.fod._common.session.cli.mixin.FoDUnirestInstanceSupplierMixin; +import com.fortify.cli.fod.release.cli.mixin.FoDReleaseByQualifiedNameOrIdResolverMixin; + +import kong.unirest.UnirestInstance; +import lombok.Getter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Mixin; +import picocli.CommandLine.Option; + +@Command(name = OutputHelperMixins.WaitFor.CMD_NAME) +public final class FoDReleaseWaitForCommand extends AbstractWaitForCommand { + @Getter @Mixin private FoDUnirestInstanceSupplierMixin unirestInstanceSupplier; + @Mixin private FoDDelimiterMixin delimiterMixin; // Is automatically injected in resolver mixins + @Mixin private FoDReleaseByQualifiedNameOrIdResolverMixin.PositionalParameterMulti releaseResolver; + @Option(names={"-s", "--suspended"}, paramLabel="true|false", required=true, defaultValue="false", arity="1") + private boolean suspended; + + @Override + protected final WaitHelperBuilder configure(UnirestInstance unirest, WaitHelperBuilder builder) { + return builder + .recordsSupplier(releaseResolver::getReleaseDescriptorJsonNodes) + .currentStateProperty("suspended") + .knownStates("true", "false") + .failureStates() + .matchStates(Set.of(String.valueOf(suspended))); + } +} diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/mixin/FoDReleaseByQualifiedNameOrIdResolverMixin.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/mixin/FoDReleaseByQualifiedNameOrIdResolverMixin.java index c03d5b1a9a..b3305d06a8 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/mixin/FoDReleaseByQualifiedNameOrIdResolverMixin.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/release/cli/mixin/FoDReleaseByQualifiedNameOrIdResolverMixin.java @@ -13,6 +13,11 @@ package com.fortify.cli.fod.release.cli.mixin; +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.JsonNode; import com.fortify.cli.common.cli.util.EnvSuffix; import com.fortify.cli.common.util.StringUtils; import com.fortify.cli.fod._common.cli.mixin.FoDDelimiterMixin; @@ -43,6 +48,23 @@ public String getReleaseId(UnirestInstance unirest) { return descriptor==null ? null : descriptor.getReleaseId(); } } + + public static abstract class AbstractFoDMultiQualifiedReleaseNameOrIdResolverMixin implements IFoDDelimiterMixinAware { + @Setter private FoDDelimiterMixin delimiterMixin; + public abstract String[] getQualifiedReleaseNamesOrIds(); + + public FoDReleaseDescriptor[] getReleaseDescriptors(UnirestInstance unirest, String... fields) { + return Stream.of(getQualifiedReleaseNamesOrIds()).map(nameOrId->FoDReleaseHelper.getReleaseDescriptor(unirest, nameOrId, delimiterMixin.getDelimiter(), true, fields)).toArray(FoDReleaseDescriptor[]::new); + } + + public Collection getReleaseDescriptorJsonNodes(UnirestInstance unirest, String... fields) { + return Stream.of(getReleaseDescriptors(unirest, fields)).map(FoDReleaseDescriptor::asJsonNode).collect(Collectors.toList()); + } + + public Integer[] getReleaseIds(UnirestInstance unirest) { + return Stream.of(getReleaseDescriptors(unirest, "releaseId")).map(FoDReleaseDescriptor::getReleaseId).toArray(Integer[]::new); + } + } public static class RequiredOption extends AbstractFoDQualifiedReleaseNameOrIdResolverMixin { @Option(names = {"--release", "--rel"}, required = true, paramLabel = "id|app[:ms]:rel", descriptionKey = "fcli.fod.release.resolver.name-or-id") @@ -63,4 +85,9 @@ public static class OptionalCopyFromOption extends AbstractFoDQualifiedReleaseNa @Option(names = {"--copy-from"}, required = false, paramLabel = "id|app[:ms]:rel", descriptionKey = "fcli.fod.release.resolver.copy-from.nameOrId") @Getter private String qualifiedReleaseNameOrId; } + + public static class PositionalParameterMulti extends AbstractFoDMultiQualifiedReleaseNameOrIdResolverMixin { + @Parameters(index = "0", arity = "1..", paramLabel="id|app[:ms]:rel", descriptionKey = "fcli.fod.release.resolver.multi-name-or-id") + @Getter private String[] qualifiedReleaseNamesOrIds; + } } 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 2ec1371237..12a81a0332 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 @@ -41,6 +41,7 @@ fcli.fod.scan.in-progress-action = The action to use if a scan is already in pro fcli.fod.scan.remediation-preference = The remediation preference to use. Valid values: ${COMPLETION-CANDIDATES}. fcli.fod.microservice.resolver.name = Microservice name in the format :. fcli.fod.release.resolver.name-or-id = Release id or [:]: name. +fcli.fod.release.resolver.multi-name-or-id = One or more release id's or [:]: names. fcli.fod.release.resolver.name = Release name in the format [:]:. fcli.fod.import.sbom-format = The SBOM format to import. If empty Cyclone DX format is assumed. fcli.fod.user.user-name-or-id = User id or username. Note that numeric values are always interpreted \ @@ -363,7 +364,11 @@ fcli.fod.release.create.usage.header = Create a new application release. \ fcli.fod.release.create.application-name = The id or name of the application to create the release on. fcli.fod.release.create.release-name = The name of the release to create for the application. 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.copy-from = The id or name of a release to copy existing state from. Note that \ + FoD will put the release in 'suspended' state until copying is complete, during which time scan \ + requests and other operations may be rejected. If you want to run any other operations on the release, \ + it is recommended to first invoke the `fcli fod release wait-for` command to wait until the release \ + leaves suspended state. 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. \ @@ -387,6 +392,19 @@ fcli.fod.release.update.attr = Attribute id or name and its value to set on the 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. +fcli.fod.release.wait-for.usage.header = Wait for one or more scans to reach or exit suspended state. +fcli.fod.release.wait-for.usage.description.0 = Although this command offers a lot of options to \ + cover many different use cases, you can simply pass one or more release names or id's to wait until \ + those releases leave 'suspended' state. \ + %n%nMost common use case is to invoke this wait-for command after creating a new release through the \ + `fcli fod release create` command with the `--copy-from` option; FoD will put the newly created \ + release in 'suspended' state until copying is completed, during which time scan requests and other \ + operations may be rejected. \ + %n%nNote that contrary to other fcli wait-for commands, any options related to unknown or failure state \ + handling are not applicable to this wait-for command and will be ignored. +fcli.fod.release.wait-for.until = Wait until either any or all releases match. If neither --until or --while are specified, default is to wait until all releases match. +fcli.fod.release.wait-for.while = Wait while either any or all releases match. +fcli.fod.release.wait-for.suspended = Suspended state against which to match the given releases; may be `false` (default) or `true`. # fcli fod assessment-type fcli.fod.assessment-type.usage.header = Manage FoD assessment types. @@ -884,6 +902,7 @@ fcli.fod.app.output.table.options = applicationId,applicationName,fcliApplicatio fcli.fod.app.scan.output.table.options = scanId,scanType,analysisStatusType,applicationName,microserviceName,releaseName,startedDateTime,completedDateTime,scanMethodTypeName fcli.fod.microservice.output.table.options = microserviceId,microserviceName,applicationName fcli.fod.release.output.table.options = releaseId,releaseName,microserviceName,applicationName,sdlcStatusType +fcli.fod.release.wait-for.output.table.options = releaseId,releaseName,microserviceName,applicationName,suspended fcli.fod.release.scan.output.table.options = scanId,scanType,analysisStatusType,applicationName,microserviceName,releaseName,startedDateTime,completedDateTime,scanMethodTypeName fcli.fod.release.assessment-type.output.table.options = assessmentTypeId,name,scanType,frequencyType,unitInfo,entitlementId,entitlementDescription fcli.fod.entitlement.output.table.options = entitlementId,entitlementDescription,startDate,endDate,unitInfo From c54423279cfcb1764e478afdb742b45231c1375e Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:57:11 +0200 Subject: [PATCH 3/6] chore: Remove PREVIEW label from generated PR comments (already clear from documentation that this is a preview feature) --- .../com/fortify/cli/fod/actions/zip/github-pr-comment.yaml | 2 +- .../com/fortify/cli/ssc/actions/zip/github-pr-comment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/github-pr-comment.yaml b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/github-pr-comment.yaml index e01a8de039..5fe8133ad5 100644 --- a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/github-pr-comment.yaml +++ b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/github-pr-comment.yaml @@ -134,7 +134,7 @@ valueTemplates: - name: reviewBody contents: | - ## Fortify vulnerability summary (PREVIEW) + ## Fortify vulnerability summary Any issues listed below are based on comparing the latest scan results against the previous scan results in FoD release [${parameters.release.applicationName}${#isNotBlank(parameters.release.microserviceNae)?'- '+parameters.release.microserviceName:''} - ${parameters.release.releaseName}](${#fod.releaseBrowserUrl(parameters.release)}). This is for informational purposes only and, depending on workflow, may not be an accurate representation of what issues will be introduced into or removed from the target branch when merging this PR. diff --git a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/github-pr-comment.yaml b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/github-pr-comment.yaml index db5d72005a..8320cc4bd9 100644 --- a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/github-pr-comment.yaml +++ b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/actions/zip/github-pr-comment.yaml @@ -139,7 +139,7 @@ valueTemplates: - name: reviewBody contents: | - ## Fortify vulnerability summary (PREVIEW) + ## Fortify vulnerability summary Any issues listed below are based on comparing the latest scan results against the previous scan results in SSC application version [${parameters.appversion.project.name} - ${parameters.appversion.name}](${#ssc.appversionBrowserUrl(parameters.appversion,null)}). This is for informational purposes only and, depending on workflow, may not be an accurate representation of what issues will be introduced into or removed from the target branch when merging this PR. From 9a1b1bff3fe2b68fcc35f9512cf1a85f3713691c Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:22:39 +0200 Subject: [PATCH 4/6] fix: `fcli fod action run setup-release`: Add `Development` default value for `--sdlc-status` --- .../resources/com/fortify/cli/fod/actions/zip/setup-release.yaml | 1 + 1 file changed, 1 insertion(+) 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 e95182346d..0608340ebb 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 @@ -53,6 +53,7 @@ parameters: name: sdlc-status cliAliases: status description: "See `fcli fod release create`" + defaultValue: Development - group: sast_setup_opts name: assessment-type required: false From 07d0914964700adf33712f65b92817778b2178f2 Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:35:16 +0200 Subject: [PATCH 5/6] fix: `fcli fod action run setup-release`: Wait for release to exit suspended state --- .../com/fortify/cli/fod/actions/zip/setup-release.yaml | 3 +++ 1 file changed, 3 insertions(+) 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 0608340ebb..af52e9c260 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 @@ -96,6 +96,9 @@ steps: - to: stdout value: | Create application release ${parameters.release} (id ${createRelease[0].releaseId}): ${createRelease[0].__action__} + - progress: "Waiting for release to leave suspended state" + - fcli: + - args: fod release wait-for "${parameters.release}" --progress=none - if: ${parameters["scan-types"].contains("sast")} steps: - if: ${parameters.profile=="default"} From fecf423d6d4a6cb5a31445a4bdb75b5906658be5 Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:49:54 +0200 Subject: [PATCH 6/6] fix: Fix fcli command links in action documentation (fixes #622) --- .../cmd/AbstractActionAsciidocCommand.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fcli-core/fcli-common/src/main/java/com/fortify/cli/common/action/cli/cmd/AbstractActionAsciidocCommand.java b/fcli-core/fcli-common/src/main/java/com/fortify/cli/common/action/cli/cmd/AbstractActionAsciidocCommand.java index cffb960ee2..02ceecd9ab 100644 --- a/fcli-core/fcli-common/src/main/java/com/fortify/cli/common/action/cli/cmd/AbstractActionAsciidocCommand.java +++ b/fcli-core/fcli-common/src/main/java/com/fortify/cli/common/action/cli/cmd/AbstractActionAsciidocCommand.java @@ -18,7 +18,6 @@ import java.nio.file.StandardOpenOption; import java.util.HashSet; import java.util.Set; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -116,14 +115,28 @@ private final String generateOptionDescription(IOptionDescriptor descriptor) { private final String addLinks(String contents) { if ( manpageDir==null ) { return contents; } + // TODO Do we want to automatically insert fcli links (which could potentially lead to bugs as seen with + // https://github.com/fortify/fcli/issues/622), or should we allow Markdown syntax in action descriptions? + // We could either add support for new markdownDescription properties, or allow Markdown in existing + // description properties and clean this up in the 'action help' command. var manPages = listDir(manpageDir).stream().filter(s->s.matches("fcli-[\\w-]+-[\\w-]+-[\\w-]+.adoc")) .map(s->s.replaceAll("\\.adoc", "")) + .sorted((a,b)->Integer.compare(a.length(), b.length())) // In case of overlapping names, we need to replace longest matching name .collect(Collectors.toSet()); for ( var manPage : manPages ) { var pattern = manPage.replace("-", "[ -]"); var replacement = String.format("link:manpage/%s.html[$1]", manPage); - contents = contents.replaceAll("(? action run' references in synopsis + contents = contents.replaceAll("("+pattern+")", replacement); + } else { + // Replace literal 'fcli *' references embedded in backticks, if not preceded by '[' + // as that (likely) means we already generated a link for a longer command name. + // See https://github.com/fortify/fcli/issues/622 for example. The backticks need to + // go into the link text (as otherwise link:... would be rendered literally), so we + // need to include the full text between the backticks in the link text. + contents = contents.replaceAll("(?