From b096250fc54f26e7b4f3bd1d4368124acecad8e1 Mon Sep 17 00:00:00 2001 From: Keeggo AppSec <153924066+Keeggo-AppSec@users.noreply.github.com> Date: Thu, 15 Aug 2024 09:48:57 -0300 Subject: [PATCH] Add new yaml file --- .github/scripts/fortify-wait-fpr.js | 126 ++++++++++++++++++++++++++++ .github/workflows/mobb-fortify.yaml | 84 +++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 .github/scripts/fortify-wait-fpr.js create mode 100644 .github/workflows/mobb-fortify.yaml diff --git a/.github/scripts/fortify-wait-fpr.js b/.github/scripts/fortify-wait-fpr.js new file mode 100644 index 00000000..7fbc5845 --- /dev/null +++ b/.github/scripts/fortify-wait-fpr.js @@ -0,0 +1,126 @@ +const fs = require("node:fs"); +const timers = require("node:timers/promises"); +const { exec } = require("node:child_process"); +const { promisify } = require("node:util"); + +const scanId = process.argv[2]; +const FORTIFY_USER = process.env.FORTIFY_USER; +const FORTIFY_TOKEN = process.env.FORTIFY_API_TOKEN; +const FORTIFY_TENANT = process.env.FORTIFY_TENANT; + +const execAsync = promisify(exec); + +async function grepCount(pattern, filePath) { + try { + const { stdout } = await execAsync(`grep -c "${pattern}" ${filePath}`); + const count = parseInt(stdout.trim(), 10); + return count; + } catch (error) { + return 0; + } +} + +async function unzip(filePath) { + try { + const { stdout } = await execAsync(`unzip ${filePath}`); + return stdout; + } catch (error) { + console.error(`Error executing command: ${error}`); + return ""; + } +} + +async function main() { + const tokenData = await fetch("https://api.ams.fortify.com/oauth/token", { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: `grant_type=password&scope=api-tenant&username=${FORTIFY_TENANT}\\${FORTIFY_USER}&password=${FORTIFY_TOKEN}&security_code=`, + }).then((r) => r.json()); + + if (!tokenData || !tokenData.access_token) { + throw new Error("Can't authenticate, check credentials."); + } + + const token = tokenData.access_token; + + while (true) { + const summaryResponse = await fetch( + `https://api.ams.fortify.com/api/v3/scans/${scanId}/summary`, + { + headers: { Authorization: `Bearer ${token}` }, + } + ); + + if (summaryResponse.status === 200) { + const summaryData = await summaryResponse.json(); + + if (summaryData.analysisStatusType === "Completed") { + break; + } + console.log(`Scan status: ${summaryData.analysisStatusType}...`); + } else { + console.log(`Scan API status: ${summaryResponse.status}...`); + } + await timers.setTimeout(5000); + } + + let buffer; + + while (true) { + const fileResponse = await fetch( + `https://api.ams.fortify.com/api/v3/scans/${scanId}/fpr`, + { + headers: { Authorization: `Bearer ${token}` }, + } + ); + + if (fileResponse.status === 200) { + buffer = await fileResponse.arrayBuffer(); + break; + } + + if ([202, 429].includes(fileResponse.status)) { + console.log(`Waiting FPR file...`); + await timers.setTimeout(5000); + } else { + throw new Error( + `Unexpected status code from fpr endpoint: ${fileResponse.status}.` + ); + } + } + + fs.writeFileSync("./scandata.fpr", Buffer.from(buffer)); + + await unzip("./scandata.fpr"); + + const numberOfInfoSevIssues = await grepCount( + "1.0", + "audit.fvdl" + ); + const numberOfLowSevIssues = await grepCount( + "2.0", + "audit.fvdl" + ); + const numberOfMediumSevIssues = await grepCount( + "3.0", + "audit.fvdl" + ); + const numberOfHighSevIssues = await grepCount( + "4.0", + "audit.fvdl" + ); + const numberOfCriticalSevIssues = await grepCount( + "5.0", + "audit.fvdl" + ); + const hasBlockingIssues = + numberOfCriticalSevIssues > 0 || numberOfHighSevIssues > 0; + console.log( + `Scan complete, number of info severity issues: ${numberOfInfoSevIssues}, number of low severity issues: ${numberOfLowSevIssues}, number of medium severity issues: ${numberOfMediumSevIssues}, number of high severity issues: ${numberOfHighSevIssues}, number of critical severity issues: ${numberOfCriticalSevIssues}` + ); + if (hasBlockingIssues) { + process.exit(1); + } +} + +main().catch(console.error); diff --git a/.github/workflows/mobb-fortify.yaml b/.github/workflows/mobb-fortify.yaml new file mode 100644 index 00000000..f9e965d4 --- /dev/null +++ b/.github/workflows/mobb-fortify.yaml @@ -0,0 +1,84 @@ +# Mobb/Fortify Fixer on pull requests +# This workflow defines the needed steps to run Fortify on every pull request and pass the results to Mobb Fixer. +# +# Secrets in use (add your missing ones): +# FORTIFY_USER - your Fortify username +# FORTIFY_TENANT - your Fortify tenant name +# FORTIFY_API_TOKEN - your Fortify access token +# FORTIFY_RELEASE_ID - your Fortify app's release-id (number) +# MOBB_API_TOKEN - your mobb user credentials (autumatially set if you forked this repo via the Mobb app) +# GITHUB_TOKEN - automatically set by GitHub + +name: "Mobb/Fortify" + +on: + pull_request: + branches: ["*"] + +jobs: + scan-and-fix: + name: Scan with Fortify and fix with Mobb + runs-on: 'ubuntu-latest' + timeout-minutes: 360 + permissions: + pull-requests: write + statuses: write + contents: read + actions: read + + steps: + - name: Setup Java on this machine + uses: actions/setup-java@v3 + with: + distribution: "oracle" + java-version: "19" + - name: Setup Maven on this machine + uses: stCarolas/setup-maven@v4.5 + with: + maven-version: 3.8.6 + - name: Setup Node on this machine + uses: actions/setup-node@v3.6.0 + with: + node-version: 18 + - name: Install sed + run: sudo apt-get update && sudo apt-get install -y sed + + - name: Checkout repo to get code + uses: actions/checkout@v3 + + - name: Download Fortify uploader CLI + run: | + wget https://tools.fortify.com/scancentral/Fortify_ScanCentral_Client_21.2.0_x64.zip -O fcs.zip + unzip fcs.zip + chmod +x bin/scancentral + wget https://github.com/fod-dev/fod-uploader-java/releases/download/v5.4.0/FodUpload.jar -O FodUpload.jar + + - name: Run Fortify SAST scan + run: | + ./bin/scancentral package -bt mvn -o fortify_package.zip + UPLOAD_OUTPUT=$(java -jar FodUpload.jar \ + -z fortify_package.zip \ + -ep SingleScanOnly \ + -portalurl https://ams.fortify.com/ \ + -apiurl https://api.ams.fortify.com/ \ + -userCredentials ${{ secrets.FORTIFY_USER }} ${{ secrets.FORTIFY_API_TOKEN }} \ + -tenantCode ${{ secrets.FORTIFY_TENANT }} \ + -releaseId ${{ secrets.FORTIFY_RELEASE_ID }} \ + -pp Queue) + SCAN_ID=$(echo "$UPLOAD_OUTPUT" | sed -n 's/Scan \([0-9]*\).*$/\1/p') + FORTIFY_USER=${{ secrets.FORTIFY_USER }} FORTIFY_API_TOKEN=${{ secrets.FORTIFY_API_TOKEN }} FORTIFY_TENANT=${{ secrets.FORTIFY_TENANT }} node .github/scripts/fortify-wait-fpr.js "$SCAN_ID" + - name: Archive fpr + if: always() + uses: actions/upload-artifact@v3 + with: + name: fpr + path: scandata.fpr + + - name: Run Mobb on the findings and get fixes + if: always() + uses: mobb-dev/action/review@v1.1 + with: + report-file: "scandata.fpr" + api-key: ${{ secrets.MOBB_API_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} + scanner: fortify