diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/output/cli/mixin/FoDOutputHelperMixins.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/output/cli/mixin/FoDOutputHelperMixins.java index a30030196f..041a2e2ad5 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/output/cli/mixin/FoDOutputHelperMixins.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/output/cli/mixin/FoDOutputHelperMixins.java @@ -84,6 +84,9 @@ public static class ImportMobile extends OutputHelperMixins.TableNoQuery { public static class ImportOpenSource extends OutputHelperMixins.TableNoQuery { public static final String CMD_NAME = "import-open-source"; } + public static class ImportSarif extends OutputHelperMixins.TableNoQuery { + public static final String CMD_NAME = "import-sarif"; + } public static class Lookup extends OutputHelperMixins.TableWithQuery { public static final String CMD_NAME = "lookup"; diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/rest/FoDUrls.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/rest/FoDUrls.java index fa16b04832..9785e30930 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/rest/FoDUrls.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/rest/FoDUrls.java @@ -42,6 +42,7 @@ public class FoDUrls { public static final String RELEASE_SCANS = RELEASE + "/scans"; public static final String STATIC_SCANS = ApiBase + "/releases/{relId}/static-scans"; public static final String STATIC_SCANS_IMPORT = STATIC_SCANS + "/import-scan"; + public static final String STATIC_SCANS_IMPORT_SARIF = STATIC_SCANS + "/import-sarif"; public static final String STATIC_SCAN_START = STATIC_SCANS + "/start-scan"; public static final String STATIC_SCAN_START_WITH_DEFAULTS = STATIC_SCANS + "/start-scan-with-defaults"; public static final String STATIC_SCAN_START_ADVANCED = STATIC_SCANS + "/start-scan-advanced"; diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/scan/helper/FoDScanDescriptor.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/scan/helper/FoDScanDescriptor.java index e286ad88f0..e3e1258c2e 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/scan/helper/FoDScanDescriptor.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/_common/scan/helper/FoDScanDescriptor.java @@ -12,12 +12,17 @@ */ package com.fortify.cli.fod._common.scan.helper; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.formkiq.graalvm.annotations.Reflectable; import com.fortify.cli.common.json.JsonNodeHolder; +import com.fortify.cli.fod.attribute.helper.FoDAttributeDescriptor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -35,6 +40,7 @@ public class FoDScanDescriptor extends JsonNodeHolder { private String microserviceName; private String analysisStatusType; private String status; + private ArrayList attributes; @JsonIgnore public String getReleaseAndScanId() { @@ -45,4 +51,15 @@ public String getReleaseAndScanId() { private Date startedDateTime; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyy-MM-dd'T'hh:mm:ss") private Date completedDateTime; + + public Map attributesAsMap() { + if (attributes == null || attributes.isEmpty()) { + return Collections.emptyMap(); + } + Map attrMap = new HashMap<>(); + for (FoDAttributeDescriptor attr : attributes) { + attrMap.put(attr.getId(), attr.getValue()); + } + return Collections.unmodifiableMap(attrMap); + } } 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 826c0a560a..919384af7a 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 @@ -633,4 +633,28 @@ public static java.util.Optional resolveValue(String input) { } } + public enum SBOMFormat implements IFoDEnumValueSupplier { + CycloneDX("CycloneDX"), + SPDX("SPDX"); + + public final String value; + + SBOMFormat(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + + /** + * Resolve an input string which may be either the enum constant name (e.g. "CycloneDX") + * or the user-facing value (e.g. "CycloneDX") to the canonical user-facing value. + * Comparison for the enum name is case-insensitive. Returns an empty Optional when no match. + */ + public static java.util.Optional resolveValue(String input) { + return IFoDEnumValueSupplier.resolveEnumValue(input, values()); + } + } + } diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadCommand.java index 238f35cf38..51afd6c665 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadCommand.java @@ -16,24 +16,32 @@ import com.fortify.cli.fod._common.scan.cli.cmd.AbstractFoDScanDownloadCommand; import com.fortify.cli.fod._common.scan.helper.FoDScanDescriptor; import com.fortify.cli.fod._common.scan.helper.FoDScanType; +import com.fortify.cli.fod._common.util.FoDEnums; import kong.unirest.GetRequest; import kong.unirest.UnirestInstance; import lombok.Getter; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; +import picocli.CommandLine.Option; @Command(name = OutputHelperMixins.Download.CMD_NAME) public class FoDOssScanDownloadCommand extends AbstractFoDScanDownloadCommand { @Getter @Mixin private OutputHelperMixins.Download outputHelper; - + @Option(names="--format", required = false, defaultValue = "CycloneDX") + private FoDEnums.SBOMFormat format; + @Override protected GetRequest getDownloadRequest(UnirestInstance unirest, FoDScanDescriptor scanDescriptor) { - return unirest.get("/api/v3/open-source-scans/{scanId}/sbom") - .routeParam("scanId", scanDescriptor.getScanId()) - .accept("application/octet-stream"); + String path = "/api/v3/open-source-scans/{scanId}/sbom"; + GetRequest req = unirest.get(path) + .routeParam("scanId", scanDescriptor.getScanId()); + if ( format != null ) { + req = req.queryString("format", format.getValue()); + } + return req.accept("application/octet-stream"); } - + @Override protected FoDScanType getScanType() { return FoDScanType.OpenSource; diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadLatestCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadLatestCommand.java index b1f7dbf633..2a592da86c 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadLatestCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/oss_scan/cli/cmd/FoDOssScanDownloadLatestCommand.java @@ -16,6 +16,7 @@ import com.fortify.cli.fod._common.scan.cli.cmd.AbstractFoDScanDownloadLatestCommand; import com.fortify.cli.fod._common.scan.helper.FoDScanDescriptor; import com.fortify.cli.fod._common.scan.helper.FoDScanType; +import com.fortify.cli.fod._common.util.FoDEnums; import com.fortify.cli.fod.release.helper.FoDReleaseDescriptor; import kong.unirest.GetRequest; @@ -23,15 +24,23 @@ import lombok.Getter; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; +import picocli.CommandLine.Option; @Command(name = FoDOutputHelperMixins.DownloadLatest.CMD_NAME) public class FoDOssScanDownloadLatestCommand extends AbstractFoDScanDownloadLatestCommand { @Getter @Mixin private FoDOutputHelperMixins.DownloadLatest outputHelper; + @Option(names="--format", required = false, defaultValue = "CycloneDX") + private FoDEnums.SBOMFormat format; @Override protected GetRequest getDownloadRequest(UnirestInstance unirest, FoDReleaseDescriptor releaseDescriptor, FoDScanDescriptor scanDescriptor) { - return unirest.get("/api/v3/open-source-scans/{scanId}/sbom") - .routeParam("scanId", scanDescriptor.getScanId()); + String path = "/api/v3/open-source-scans/{scanId}/sbom"; + GetRequest req = unirest.get(path) + .routeParam("scanId", scanDescriptor.getScanId()); + if ( format != null ) { + req = req.queryString("format", format.getValue()); + } + return req.accept("application/octet-stream"); } @Override diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanCommands.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanCommands.java index 1f70d0d7c0..3b1becb7e7 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanCommands.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanCommands.java @@ -25,6 +25,7 @@ FoDSastScanGetCommand.class, FoDSastScanGetConfigCommand.class, FoDSastScanImportCommand.class, + FoDSastScanImportSarifCommand.class, FoDSastScanListCommand.class, FoDSastScanSetupCommand.class, FoDSastScanStartCommand.class, diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanImportSarifCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanImportSarifCommand.java new file mode 100644 index 0000000000..0bd974fe45 --- /dev/null +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanImportSarifCommand.java @@ -0,0 +1,39 @@ +/* + * 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.sast_scan.cli.cmd; + +import com.fortify.cli.fod._common.output.cli.mixin.FoDOutputHelperMixins; +import com.fortify.cli.fod._common.rest.FoDUrls; +import com.fortify.cli.fod._common.scan.cli.cmd.AbstractFoDScanImportCommand; +import com.fortify.cli.fod._common.scan.helper.FoDScanType; + +import kong.unirest.HttpRequest; +import kong.unirest.UnirestInstance; +import lombok.Getter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Mixin; + +@Command(name = FoDOutputHelperMixins.ImportSarif.CMD_NAME) +public class FoDSastScanImportSarifCommand extends AbstractFoDScanImportCommand { + @Getter @Mixin private FoDOutputHelperMixins.ImportSarif outputHelper; + + @Override + protected HttpRequest getBaseRequest(UnirestInstance unirest, String releaseId) { + return unirest.put(FoDUrls.STATIC_SCANS_IMPORT_SARIF).routeParam("relId", releaseId); + } + + @Override + protected FoDScanType getScanType() { + return FoDScanType.Static; + } +} 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 107e94e051..82bbae8115 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 @@ -567,6 +567,9 @@ fcli.fod.sast-scan.setup.use-aviator = Use Fortify Aviator to audit results and fcli.fod.sast-scan.import.usage.header = Import existing SAST scan results (from an FPR file). fcli.fod.sast-scan.import.usage.description = As FoD doesn't return a scan id for imported scans, the output of this command cannot be used with commands that expect a scan id, like the wait-for command. fcli.fod.sast-scan.import.file = FPR file containing existing SAST scan results to be imported. +fcli.fod.sast-scan.import-sarif.usage.header = Import existing SAST scan results (from a SARIF file). +fcli.fod.sast-scan.import-sarif.usage.description = As FoD doesn't return a scan id for imported scans, the output of this command cannot be used with commands that expect a scan id, like the wait-for command. +fcli.fod.sast-scan.import-sarif.file = SARIF file containing existing SAST scan results to be imported. fcli.fod.sast-scan.download.usage.header = Download scan results. fcli.fod.sast-scan.download.file = File path and name where to save the FPR file. fcli.fod.sast-scan.download-latest.usage.header = Download latest scan results from release. @@ -867,8 +870,10 @@ fcli.fod.oss-scan.wait-for.while = ${fcli.fod.scan.wait-for.while} fcli.fod.oss-scan.wait-for.any-state = ${fcli.fod.scan.wait-for.any-state} fcli.fod.oss-scan.download.usage.header = Download scan results. fcli.fod.oss-scan.download.file = File path and name where to save the SBOM file. +fcli.fod.oss-scan.download.format = Open Source scan results file format. Valid values: ${COMPLETION-CANDIDATES} (default value is CycloneDX). fcli.fod.oss-scan.download-latest.usage.header = Download latest scan results from release. fcli.fod.oss-scan.download-latest.file = File path and name where to save the SBOM file. +fcli.fod.oss-scan.download-latest.format = Open Source scan results file format. Valid values: ${COMPLETION-CANDIDATES} (default value is CycloneDX). # fcli fod issue fcli.fod.issue.usage.header = Manage FoD issues (vulnerabilities) and related entities.