diff --git a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanSetupCommand.java b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanSetupCommand.java index 3892889628..5c42340bda 100644 --- a/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanSetupCommand.java +++ b/fcli-core/fcli-fod/src/main/java/com/fortify/cli/fod/sast_scan/cli/cmd/FoDSastScanSetupCommand.java @@ -81,92 +81,113 @@ public class FoDSastScanSetupCommand extends AbstractFoDJsonNodeOutputCommand im private final Boolean includeThirdPartyLibraries = false; @Option(names = {"--use-source-control"}) private final Boolean useSourceControl = false; + @Option(names={"--skip-if-exists"}) + private boolean skipIfExists = false; - @Mixin - private ProgressWriterFactoryMixin progressWriterFactory; + // TODO We don't actually use a progress writer, but for now we can't + // remove the --progress option to maintain backward compatibility. + // This should be removed once we move to fcli 3.x (and probably + // check other FoD commands as well), unless we actually start + // using this of course. + @Mixin private ProgressWriterFactoryMixin progressWriterFactory; - // TODO Split into multiple methods @Override public JsonNode getJsonNode(UnirestInstance unirest) { try (var progressWriter = progressWriterFactory.create()) { var releaseDescriptor = releaseResolver.getReleaseDescriptor(unirest); - var relId = releaseDescriptor.getReleaseId(); - Integer entitlementIdToUse = 0; - Integer assessmentTypeId = 0; - Integer technologyStackId = 0; - Integer languageLevelId = 0; - - // get current setup - FoDScanConfigSastDescriptor currentSetup = FoDScanSastHelper.getSetupDescriptor(unirest, relId); - - LOG.info("Finding appropriate entitlement to use."); - - // find an appropriate assessment type to use - Optional atd = Arrays.stream( - FoDReleaseAssessmentTypeHelper.getAssessmentTypes(unirest, - relId, FoDScanType.Static, - entitlementFrequencyTypeMixin.getEntitlementFrequencyType(), - false, true) - ).filter(n -> n.getName().equals(staticAssessmentType)) - .findFirst(); - if (atd.isEmpty()) { - throw new IllegalArgumentException("Cannot find appropriate assessment type for specified options."); - } - assessmentTypeId = atd.get().getAssessmentTypeId(); - entitlementIdToUse = atd.get().getEntitlementId(); - - // validate entitlement specified or currently in use against assessment type found - if (entitlementId != null && entitlementId > 0) { - // check if "entitlement id" explicitly matches what has been found - if (!Objects.equals(entitlementIdToUse, entitlementId)) { - throw new IllegalArgumentException("Cannot appropriate assessment type for use with entitlement: " + entitlementId); - } + var setupDescriptor = FoDScanSastHelper.getSetupDescriptor(unirest, releaseDescriptor.getReleaseId()); + if ( skipIfExists && setupDescriptor.getAssessmentTypeId()!=0 ) { + return setupDescriptor.asObjectNode().put("__action__", "SKIPPED_EXISTING"); } else { - if (currentSetup.getEntitlementId() != null && currentSetup.getEntitlementId() > 0) { - // check if "entitlement id" is already configured - if (!Objects.equals(entitlementIdToUse, currentSetup.getEntitlementId())) { - LOG.warn("Changing current release entitlement from " + currentSetup.getEntitlementId()); - } - } + return setup(unirest, releaseDescriptor, setupDescriptor).asObjectNode(); } - LOG.info("Configuring release to use entitlement " + entitlementIdToUse); + } + } - // check if the entitlement is still valid - FoDReleaseAssessmentTypeHelper.validateEntitlement(relId, atd.get()); - LOG.info("The entitlement " + entitlementIdToUse + " is valid."); + private FoDScanConfigSastDescriptor setup(UnirestInstance unirest, FoDReleaseDescriptor releaseDescriptor, FoDScanConfigSastDescriptor currentSetup) { + var relId = releaseDescriptor.getReleaseId(); - // find/check technology stack / language level - FoDLookupDescriptor lookupDescriptor = null; + LOG.info("Finding appropriate entitlement to use."); + + var atd = getAssessmentTypeDescriptor(unirest, relId); + var assessmentTypeId = atd.getAssessmentTypeId(); + var entitlementIdToUse = atd.getEntitlementId(); + + validateEntitlement(currentSetup, entitlementIdToUse, relId, atd); + LOG.info("Configuring release to use entitlement " + entitlementIdToUse); + + var technologyStackId = getTechnologyStackId(unirest); + var languageLevelId = getLanguageLevelId(unirest, technologyStackId); + + FoDScanConfigSastSetupRequest setupSastScanRequest = FoDScanConfigSastSetupRequest.builder() + .entitlementId(entitlementIdToUse) + .assessmentTypeId(assessmentTypeId) + .entitlementFrequencyType(entitlementFrequencyTypeMixin.getEntitlementFrequencyType().name()) + .technologyStackId(technologyStackId) + .languageLevelId(languageLevelId) + .performOpenSourceAnalysis(performOpenSourceAnalysis) + .auditPreferenceType(auditPreferenceType.name()) + .includeThirdPartyLibraries(includeThirdPartyLibraries) + .useSourceControl(useSourceControl).build(); + + return FoDScanConfigSastHelper.setupScan(unirest, releaseDescriptor, setupSastScanRequest); + } + + private Integer getLanguageLevelId(UnirestInstance unirest, Integer technologyStackId) { + Integer languageLevelId = 0; + FoDLookupDescriptor lookupDescriptor = null; + if (languageLevel != null && languageLevel.length() > 0) { try { - lookupDescriptor = FoDLookupHelper.getDescriptor(unirest, FoDLookupType.TechnologyTypes, technologyStack, true); + lookupDescriptor = FoDLookupHelper.getDescriptor(unirest, FoDLookupType.LanguageLevels, String.valueOf(technologyStackId), languageLevel, true); } catch (JsonProcessingException ex) { throw new IllegalStateException(ex.getMessage()); } - if (lookupDescriptor != null) technologyStackId = Integer.valueOf(lookupDescriptor.getValue()); - //System.out.println("technologyStackId = " + technologyStackId); - if (languageLevel != null && languageLevel.length() > 0) { - try { - lookupDescriptor = FoDLookupHelper.getDescriptor(unirest, FoDLookupType.LanguageLevels, String.valueOf(technologyStackId), languageLevel, true); - } catch (JsonProcessingException ex) { - throw new IllegalStateException(ex.getMessage()); + if (lookupDescriptor != null) languageLevelId = Integer.valueOf(lookupDescriptor.getValue()); + } + return languageLevelId; + } + + private Integer getTechnologyStackId(UnirestInstance unirest) { + // find/check technology stack / language level + FoDLookupDescriptor lookupDescriptor = null; + try { + lookupDescriptor = FoDLookupHelper.getDescriptor(unirest, FoDLookupType.TechnologyTypes, technologyStack, true); + } catch (JsonProcessingException ex) { + throw new IllegalStateException(ex.getMessage()); + } + // TODO return 0 or null, or throw exception? + return lookupDescriptor==null ? 0 : Integer.valueOf(lookupDescriptor.getValue()); + } + + private void validateEntitlement(FoDScanConfigSastDescriptor currentSetup, Integer entitlementIdToUse, String relId, FoDReleaseAssessmentTypeDescriptor atd) { + // validate entitlement specified or currently in use against assessment type found + if (entitlementId != null && entitlementId > 0) { + // check if "entitlement id" explicitly matches what has been found + if (!Objects.equals(entitlementIdToUse, entitlementId)) { + throw new IllegalArgumentException("Cannot appropriate assessment type for use with entitlement: " + entitlementId); + } + } else { + if (currentSetup.getEntitlementId() != null && currentSetup.getEntitlementId() > 0) { + // check if "entitlement id" is already configured + if (!Objects.equals(entitlementIdToUse, currentSetup.getEntitlementId())) { + LOG.warn("Changing current release entitlement from " + currentSetup.getEntitlementId()); } - if (lookupDescriptor != null) languageLevelId = Integer.valueOf(lookupDescriptor.getValue()); - //System.out.println("languageLevelId = " + languageLevelId); } - - FoDScanConfigSastSetupRequest setupSastScanRequest = FoDScanConfigSastSetupRequest.builder() - .entitlementId(entitlementIdToUse) - .assessmentTypeId(assessmentTypeId) - .entitlementFrequencyType(entitlementFrequencyTypeMixin.getEntitlementFrequencyType().name()) - .technologyStackId(technologyStackId) - .languageLevelId(languageLevelId) - .performOpenSourceAnalysis(performOpenSourceAnalysis) - .auditPreferenceType(auditPreferenceType.name()) - .includeThirdPartyLibraries(includeThirdPartyLibraries) - .useSourceControl(useSourceControl).build(); - - return FoDScanConfigSastHelper.setupScan(unirest, releaseDescriptor, setupSastScanRequest).asJsonNode(); } + // check if the entitlement is still valid + FoDReleaseAssessmentTypeHelper.validateEntitlement(relId, atd); + } + + private FoDReleaseAssessmentTypeDescriptor getAssessmentTypeDescriptor(UnirestInstance unirest, String relId) { + // find an appropriate assessment type to use + Optional atd = Arrays.stream( + FoDReleaseAssessmentTypeHelper.getAssessmentTypes(unirest, + relId, FoDScanType.Static, + entitlementFrequencyTypeMixin.getEntitlementFrequencyType(), + false, true) + ).filter(n -> n.getName().equals(staticAssessmentType)) + .findFirst(); + return atd.orElseThrow(()->new IllegalArgumentException("Cannot find appropriate assessment type for specified options.")); } @Override 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 c5a86f16ad..cd4d0c39fb 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 @@ -493,6 +493,8 @@ fcli.fod.sast-scan.setup.oss = Perform Open Source Analysis scan. fcli.fod.sast-scan.setup.audit-preference = Audit preference, e.g. Manual or Automated fcli.fod.sast-scan.setup.include-third-party-libs = Indicates if third party libraries should be included. fcli.fod.sast-scan.setup.use-source-control = Indicates if source control should be used. +fcli.fod.sast-scan.setup.skip-if-exists = Skip setup if a scan has already been set up. If not specified, any existing scan \ + setup will be replaced based on the given setup options. 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.