diff --git a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/fortify-aviator-report.yaml b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/fortify-aviator-report.yaml new file mode 100644 index 0000000000..49a8796683 --- /dev/null +++ b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/fortify-aviator-report.yaml @@ -0,0 +1,160 @@ +# yaml-language-server: $schema=https://fortify.github.io/fcli/schemas/action/fcli-action-schema-dev.json + +author: Fortify +usage: + header: (PREVIEW) Generate Fortify Aviator Recommendations Report. + description: | + This action generates a report on the use of Fortify Aviator to audit issues and + provide remediation guidance. Based on user feedback on this initial version of this action, + parameters and output of this action may change in the next couple of fcli releases. + +defaults: + requestTarget: fod + +parameters: + - name: file + cliAliases: f + description: "Optional output file name (or 'stdout' / 'stderr'). Default value: stdout" + required: false + defaultValue: stdout + - name: release + cliAliases: rel + description: "Required release id or :[:]" + type: release_single + - name: includeSuppressed + cliAliases: include-suppressed + description: "Set this to 'true' to include any issues that have been automatically suppressed by Fortify Aviator" + required: false + defaultValue: false + - name: includeClosed + cliAliases: include-closed + description: "Set this to 'true' to include any issues that have been closed but previously audited by Fortify Aviator" + required: false + defaultValue: false + +steps: + - set: + # Add short alias for release object, as we reference it a lot + - name: r + value: ${parameters.release} + # Define output date format + - name: dateFmt + value: YYYY-MM-dd HH:mm + # Number of vulnerabilities triaged by Aviator + - name: criticalCount + value: ${0} + - name: highCount + value: ${0} + - name: mediumCount + value: ${0} + - name: lowCount + value: ${0} + - name: aviatorCount + value: ${0} + - name: suppressedCount + value: ${0} + - name: closedCount + value: ${0} + - progress: Processing Issues + - requests: + - name: vulnerabilities + uri: /api/v3/releases/${r.releaseId}/vulnerabilities?limit=50 + query: + orderBy: severity + fortifyAviator: true + includeSuppressed: ${parameters.includeSuppressed} + includeFixed: ${parameters.includeClosed} + type: paged + onResponse: + - if: ${vulnerabilities_raw.totalCount>10000} + throw: There are too many issues to show. + forEach: + name: vulnerability + embed: + # TODO: embed more information if anything else decided to be shown? + # - name: details + # uri: /api/v3/releases/${r.releaseId}/vulnerabilities/${vulnerability.vulnId}/details + - name: comments + uri: /api/v3/releases/${r.releaseId}/vulnerabilities/${vulnerability.vulnId}/comments + # - name: history + # uri: /api/v3/releases/${r.releaseId}/vulnerabilities/${vulnerability.vulnId}/history + do: + - set: + - name: criticalCount + value: ${criticalCount + 1} + if: ${vulnerability.severityString=='Critical'} + - name: highCount + value: ${highCount + 1} + if: ${vulnerability.severityString=='High'} + - name: mediumCount + value: ${mediumlCount + 1} + if: ${vulnerability.severityString=='Medium'} + - name: lowCount + value: ${lowCount + 1} + if: ${vulnerability.severityString=='Low'} + - name: aviatorCount + value: ${aviatorCount + 1} + - name: suppressedCount + value: ${suppressedCount + 1} + if: ${vulnerability.isSuppressed==true} + - name: closedCount + value: ${closedCount + 1} + if: ${vulnerability.isClosed==true} + - append: + - name: mdVulnerabilities + valueTemplate: mdVulnerabilitiesListItem + + - write: + - to: ${parameters['file']} + valueTemplate: report + - if: ${parameters.file!='stdout'} + to: stdout + value: | + Report written to ${parameters['file']} + +valueTemplates: + - name: report + contents: | + ## Fortify Aviator Audit and Remediation Report + + ## [${r.applicationName}${#isNotBlank(r.microserviceName)?'- '+r.microserviceName:''} - ${r.releaseName}](${#fod.releaseBrowserUrl(r)}) + + Report generated on: ${#formatDateTime(dateFmt)} + + ### Summary + + Fortify Aviator audited the following number of issues: + + ${aviatorCount==0 + ? "* No issues were audited by Fortify Aviator." + : ("| Critical | High | Medium | Low | Suppressed | Closed | Total |\n" + + "| :------: | :--: | :----: | :-: | :--------: | :----: | :---: |\n" + + "|"+criticalCount+"|"+highCount+"|"+mediumCount+"|"+lowCount+"|"+suppressedCount+"*|"+closedCount+"*"+"|"+aviatorCount+"|")} + + __*__ Suppressed and Closed counts are only shown if the options `--include-suppressed` + and/or `--include-closed` are set to `true`. + + ### Details + + ${mdVulnerabilities==null + ? "* No issues were audited by Fortify Aviator." + : (#join('\n', mdVulnerabilities))} + + ${#copyright()} + + - name: mdVulnerabilitiesListItem + contents: | + #### [${vulnerability.id}](${#fod.issueBrowserUrl(vulnerability)}) - *${vulnerability.primaryLocation}* [**${vulnerability.severityString}** - ${vulnerability.category}] + ${vulnerability.isSuppressed==true?"_This issue was automatically suppressed by Fortify Aviator._

":""} + ${#htmlToText(vulnerability.comments?.^[username=='Fortify Aviator']?.comment?:'No comment.')} + + >**Status**: ${vulnerability.status}
+ >**Introduced**: ${#formatDateTime("yyyy-MM-dd", vulnerability.introducedDate)}
+ >**Developer Status**: ${vulnerability.developerStatus}
+ >**Auditor Status**: ${vulnerability.auditorStatus}
+ >**Bug Link**: ${vulnerability.bugSubmitted==true + ? vulnerability.bugLink + : "No bug submitted."} +
+ + \ No newline at end of file diff --git a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/oss-component-report.yaml b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/oss-component-report.yaml new file mode 100644 index 0000000000..44400f31be --- /dev/null +++ b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/oss-component-report.yaml @@ -0,0 +1,188 @@ +# yaml-language-server: $schema=https://fortify.github.io/fcli/schemas/action/fcli-action-schema-dev.json + +author: Fortify +usage: + header: (PREVIEW) Generate Open Source Component Report. + description: | + This action generates a report on vulnerable open source components and their security + issues for a given release. Based on user feedback on this initial version of this action, + parameters and output of this action may change in the next couple of fcli releases. + +defaults: + requestTarget: fod + +parameters: + - name: file + cliAliases: f + description: "Optional output file name (or 'stdout' / 'stderr'). Default value: stdout" + required: false + defaultValue: stdout + - name: release + cliAliases: rel + description: "Required release id or :[:]" + type: release_single + - name: scanTool + cliAliases: st + description: "Limit results to a particular Open Source scan tool, e.g. Debricked, CycloneDX. Default is all engines." + required: false + defaultValue: 'Debricked' + - name: allComponents + cliAliases: all + description: "Include all Open Source components in the results. Default is to show vulnerable components only." + required: false + defaultValue: false + +steps: + - set: + # Add short alias for release object, as we reference it a lot + - name: r + value: ${parameters.release} + # Define output date format + - name: dateFmt + value: YYYY-MM-dd HH:mm + # Number of vulnerable components + - name: vulnComps + value: ${0} + - progress: Processing Components + - requests: + - name: components + uri: /api/v3/applications/open-source-components?limit=50 + query: + openSourceScanType: ${parameters.scanTool} + orderBy: componentName + filters: releaseId:${r.releaseId} + type: paged + onResponse: + - if: ${components_raw.totalCount>10000} + throw: There are too many components to show. + forEach: + name: component + do: + - set: + - name: licensesUsed + value: ${component.licenses.size()>0?#join(', ', component.licenses.![name]):'No licenses found'} + - name: criticalCount + value: ${component.vulnerabilityCounts?.^[severity=='Critical']?.count?:0} + - name: highCount + value: ${component.vulnerabilityCounts?.^[severity=='High']?.count?:0} + - name: mediumCount + value: ${component.vulnerabilityCounts?.^[severity=='Medium']?.count?:0} + - name: lowCount + value: ${component.vulnerabilityCounts?.^[severity=='Low']?.count?:0} + - name: vulnComps + value: ${vulnComps + 1} + if: ${(criticalCount+highCount+mediumCount+lowCount) > 0 || parameters.allComponents} + - append: + - name: mdComponents + valueTemplate: mdComponentListItem + if: ${(criticalCount+highCount+mediumCount+lowCount) > 0 || parameters.allComponents} + + - progress: Processing Issues + - requests: + - name: vulnerabilities + uri: /api/v3/releases/${r.releaseId}/vulnerabilities?limit=50 + query: + filters: "category:Open Source" + orderBy: severity + type: paged + onResponse: + - if: ${vulnerabilities_raw.totalCount>10000} + throw: There are too many security issues to show. + forEach: + name: vulnerability + # TODO: embed more information if anything else decided to be shown? + #embed: + # - name: details + # uri: /api/v3/releases/${parameters.release.releaseId}/vulnerabilities/${issue.vulnId}/details + # - name: recommendations + # uri: /api/v3/releases/${parameters.release.releaseId}/vulnerabilities/${issue.vulnId}/recommendations + do: + - append: + - name: mdCriticalVulnerabilities + valueTemplate: mdVulnerabilitiesListItem + if: ${vulnerability.severityString=='Critical'} + - append: + - name: mdHighVulnerabilities + valueTemplate: mdVulnerabilitiesListItem + if: ${vulnerability.severityString=='High'} + - append: + - name: mdMediumVulnerabilities + valueTemplate: mdVulnerabilitiesListItem + if: ${vulnerability.severityString=='Medium'} + - append: + - name: mdLowVulnerabilities + valueTemplate: mdVulnerabilitiesListItem + if: ${vulnerability.severityString=='Low'} + + - write: + - to: ${parameters['file']} + valueTemplate: report + - if: ${parameters.file!='stdout'} + to: stdout + value: | + Report written to ${parameters['file']} + +valueTemplates: + - name: report + contents: | + ## Fortify on Demand Open Source Component Report + + ## [${r.applicationName}${#isNotBlank(r.microserviceName)?'- '+r.microserviceName:''} - ${r.releaseName}](${#fod.releaseBrowserUrl(r)}) + + Report generated on: ${#formatDateTime(dateFmt)} + + ### Open Source Components + + ${mdComponents==null + ? "* No components detected." + : ("| Component | Version | Type | Critical | High | Medium | Low | License | Scan Tool | \n" + + "| -------------| ------- | ---- | :------: | :--: | :----: | :-: | ------- | --------- |\n" + + #join('\n', mdComponents))} + + Showing ${vulnComps} of ${components_raw.totalCount} components in this release + + ### Security Issues + + #### Critical + + ${mdCriticalVulnerabilities==null + ? "* No Critical Security Issues detected." + : ("| CVE | Component Name | Component Version | Status | Introduced Date | Details |\n" + + "| -------------| -------------- | ----------------- | ------ | --------------- | ------- |\n" + + #join('\n', mdCriticalVulnerabilities))} + + #### High + + ${mdHighVulnerabilities==null + ? "* No High Security Issues detected." + : ("| CVE | Component Name | Component Version | Status | Introduced Date | Details |\n" + + "| -------------| -------------- | ----------------- | ------ | --------------- | ------- |\n" + + #join('\n', mdHighVulnerabilities))} + + #### Medium + + ${mdMediumVulnerabilities==null + ? "* No Medium Security Issues detected." + : ("| CVE | Component Name | Component Version | Status | Introduced Date | Details |\n" + + "| -------------| -------------- | ----------------- | ------ | --------------- | ------- |\n" + + #join('\n', mdMediumVulnerabilities))} + + #### Low + + ${mdLowVulnerabilities==null + ? "* No Low Security Issues detected." + : ("| CVE | Component Name | Component Version | Status | Introduced Date | Details |\n" + + "| -------------| -------------- | ----------------- | ------ | --------------- | ------- |\n" + + #join('\n', mdLowVulnerabilities))} + + There are a total of ${vulnerabilities_raw.totalCount} security issues in this release + + ${#copyright()} + + - name: mdComponentsList + contents: "${mdComponents}" + - name: mdComponentListItem + contents: "| ${component.componentName} | ${component.componentVersionName} | ${#substringAfter(component.packageUrl.split('/')[0], ':')} | ${criticalCount} | ${highCount} | ${mediumCount} | ${lowCount} | ${licensesUsed} | ${component.scanTool} | |" + - name: mdVulnerabilitiesListItem + contents: "| [${vulnerability.checkId}](https://www.cve.org/CVERecord?id=${vulnerability.checkId}) | ${vulnerability.primaryLocation.split('@')[0]} | ${vulnerability.primaryLocation.split('@')[1]} | ${vulnerability.status} | ${#formatDateTime(\"yyyy-MM-dd\", vulnerability.introducedDate)} | [Details](${#fod.issueBrowserUrl(vulnerability)}) |" + \ No newline at end of file diff --git a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/release-summary.yaml b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/release-summary.yaml index bf16abb75d..227a8e8980 100644 --- a/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/release-summary.yaml +++ b/fcli-core/fcli-fod/src/main/resources/com/fortify/cli/fod/actions/zip/release-summary.yaml @@ -30,7 +30,7 @@ steps: # Define output date format - name: dateFmt value: YYYY-MM-dd HH:mm - # Note: change/remove the following when Open Source counts are available on the release object + # Update Note: although open source counts are now available on the release object, the date of last scan is not so still need the first part - progress: Loading Scans - requests: - name: scans @@ -50,32 +50,6 @@ steps: value: ${scan.scanType} - name: ossScanDate value: ${scan.completedDateTime} - #- write: - # - to: stdout - # value: ${scanType} - ${ossScanDate} - - progress: Loading Vulnerabilities - - requests: - - name: issues - if: ${ossScanDate!=null} - uri: /api/v3/releases/${r.releaseId}/vulnerabilities?filters=category%3AOpen%20Source&limit=1 - onResponse: - - steps: - - set: - - name: ossTotal - value: ${issues_raw.totalCount} - - set: - - name: ossCritical - value: ${issues_raw.filters.^[#this.fieldName == 'severity']?.fieldFilterValues?.^[#this.value == "Critical"]?.count?:0} - - set: - - name: ossHigh - value: ${issues_raw.filters.^[#this.fieldName == 'severity']?.fieldFilterValues?.^[#this.value == "High"]?.count?:0} - - set: - - name: ossMedium - value: ${issues_raw.filters.^[#this.fieldName == 'severity']?.fieldFilterValues?.^[#this.value == "Medium"]?.count?:0} - - set: - - name: ossLow - value: ${issues_raw.filters.^[#this.fieldName == 'severity']?.fieldFilterValues?.^[#this.value == "Low"]?.count?:0} - # replace up to here - write: - to: ${parameters.file} @@ -85,7 +59,7 @@ steps: value: | Output written to ${parameters.file} -# Note: update references when Open Source counts are available on the release object, e.g. r.ossScanDate, r.ossCritical ... +# Note: update ossScanDate when it is available on release object ... valueTemplates: - name: summary-md contents: | @@ -105,7 +79,7 @@ valueTemplates: | **Static** | ${(#isBlank(r.staticScanDate)?#fmt('%-16s', 'N/A'):#formatDateTime(dateFmt, r.staticScanDate)) +' | '+#fmt('%8s', r.staticCritical) +' | '+#fmt('%8s', r.staticHigh) +' | '+#fmt('%8s', r.staticMedium) +' | '+#fmt('%8s', r.staticLow) +' |'} | **Dynamic** | ${(#isBlank(r.dynamicScanDate)?#fmt('%-16s', 'N/A'):#formatDateTime(dateFmt, r.dynamicScanDate))+' | '+#fmt('%8s', r.dynamicCritical) +' | '+#fmt('%8s', r.dynamicHigh) +' | '+#fmt('%8s', r.dynamicMedium) +' | '+#fmt('%8s', r.dynamicLow) +' |'} | **Mobile** | ${(#isBlank(r.mobileScanDate)?#fmt('%-16s', 'N/A'):#formatDateTime(dateFmt, r.mobileScanDate)) +' | '+#fmt('%8s', r.mobileCritical) +' | '+#fmt('%8s', r.mobileHigh) +' | '+#fmt('%8s', r.mobileMedium) +' | '+#fmt('%8s', r.mobileLow) +' |'} - | **Open Source** | ${(#isBlank(ossScanDate)?#fmt('%-16s', 'N/A'):#formatDateTime(dateFmt, ossScanDate)) +' | '+#fmt('%8s', (ossCritical!=null?ossCritical:0)) +' | '+#fmt('%8s', (ossHigh!=null?ossHigh:0)) +' | '+#fmt('%8s', (ossMedium!=null?ossMedium:0)) +' | '+#fmt('%8s', (ossLow!=null?ossLow:0)) +' |'} - | **Total** | | ${#fmt('%8s', r.staticCritical+r.dynamicCritical+r.mobileCritical+(ossCritical!=null?ossCritical:0))+' | '+#fmt('%8s', r.staticHigh+r.dynamicHigh+r.mobileHigh+(ossHigh!=null?ossHigh:0))+' | '+#fmt('%8s', r.staticMedium+r.dynamicMedium+r.mobileMedium+(ossMedium!=null?ossMedium:0))+' | '+#fmt('%8s', r.staticLow+r.dynamicLow+r.mobileLow+(ossLow!=null?ossLow:0))+' |'} + | **Open Source** | ${(#isBlank(ossScanDate)?#fmt('%-16s', 'N/A'):#formatDateTime(dateFmt, ossScanDate)) +' | '+#fmt('%8s', r.openSourceCritical) +' | '+#fmt('%8s', r.openSourceHigh) +' | '+#fmt('%8s', r.openSourceMedium) +' | '+#fmt('%8s', r.openSourceLow) +' |'} + | **Total** | | ${#fmt('%8s', r.staticCritical+r.dynamicCritical+r.mobileCritical+r.openSourceCritical)+' | '+#fmt('%8s', r.staticHigh+r.dynamicHigh+r.mobileHigh+r.openSourceHigh)+' | '+#fmt('%8s', r.staticMedium+r.dynamicMedium+r.mobileMedium+r.openSourceMedium)+' | '+#fmt('%8s', r.staticLow+r.dynamicLow+r.mobileLow+r.openSourceLow)+' |'} \ No newline at end of file