UNPKG

@zaproxy/actions-common-scans

Version:
236 lines (235 loc) 9.91 kB
import fs from "fs"; import _ from "lodash"; import readline from "readline"; import AdmZip from "adm-zip"; import { DefaultArtifactClient } from "@actions/artifact"; import { isFilteredSite } from "./models/FilteredSite.js"; import { isDifferenceSite } from "./models/DifferenceSite.js"; function createReadStreamSafe(filename) { return new Promise((resolve, reject) => { const fileStream = fs.createReadStream(filename); fileStream.on("error", reject).on("open", () => { resolve(fileStream); }); }); } const actionHelper = { getRunnerID: (body) => { const results = /RunnerID:\d+/.exec(body); if (results !== null && results.length !== 0) { return results[0].split(":")[1]; } return null; }, processLineByLine: async (tsvFile) => { const plugins = []; try { const fileStream = await createReadStreamSafe(tsvFile); const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity, }); for await (const line of rl) { if (!line.startsWith("#")) { const tmp = line.split("\t"); if (tmp[0].trim() !== "" && tmp[1].trim().toUpperCase() === "IGNORE") { plugins.push(tmp[0].trim()); } } } } catch (err) { console.error(`Error when reading the rules file: ${tsvFile} err: ${err.toString()}`); } return plugins; }, createMessage: (sites, runnerID, runnerLink) => { const NXT_LINE = "\n"; const TAB = "\t"; const BULLET = "-"; let msg = ""; const instanceCount = 5; sites.forEach((site) => { msg = msg + `${BULLET} Site: [${site["@name"]}](${site["@name"]}) ${NXT_LINE}`; if ("alerts" in site) { if (site.alerts.length !== 0) { msg = `${msg} ${TAB} **New Alerts** ${NXT_LINE}`; site.alerts.forEach((alert) => { msg = msg + TAB + `${BULLET} **${alert.name}** [${alert.pluginid}] total: ${alert.instances.length}: ${NXT_LINE}`; for (let i = 0; i < alert.instances.length; i++) { if (i >= instanceCount) { msg = msg + TAB + TAB + `${BULLET} .. ${NXT_LINE}`; break; } const instance = alert.instances[i]; msg = msg + TAB + TAB + `${BULLET} [${instance.uri}](${instance.uri}) ${NXT_LINE}`; } }); msg = msg + NXT_LINE; } } if (isDifferenceSite(site)) { if (site.removedAlerts.length !== 0) { msg = `${msg} ${TAB} **Resolved Alerts** ${NXT_LINE}`; site.removedAlerts.forEach((alert) => { msg = msg + TAB + `${BULLET} **${alert.name}** [${alert.pluginid}] total: ${alert.instances.length}: ${NXT_LINE}`; }); msg = msg + NXT_LINE; } } if (isFilteredSite(site)) { if (site.ignoredAlerts.length !== 0) { msg = `${msg} ${TAB} **Ignored Alerts** ${NXT_LINE}`; site.ignoredAlerts.forEach((alert) => { msg = msg + TAB + `${BULLET} **${alert.name}** [${alert.pluginid}] total: ${alert.instances.length}: ${NXT_LINE}`; }); msg = msg + NXT_LINE; } } msg = msg + NXT_LINE; }); if (msg.trim() !== "") { msg = msg + NXT_LINE + runnerLink; msg = msg + NXT_LINE + runnerID; } return msg; }, generateDifference: (newReport, oldReport) => { newReport.updated = false; const siteClone = []; newReport.site.forEach((newReportSite) => { // Check if the new report site already exists in the previous report const previousSite = _.filter(oldReport.site, (s) => s["@name"] === newReportSite["@name"]); // If does not exists add it to the array without further processing if (previousSite.length === 0) { newReport.updated = true; siteClone.push(newReportSite); } else { // deep clone the variable for further processing const newSite = _.clone(newReportSite); const currentAlerts = newReportSite.alerts; const previousAlerts = previousSite[0].alerts; const newAlerts = _.differenceBy(currentAlerts, previousAlerts, "pluginid"); let removedAlerts = _.differenceBy(previousAlerts, currentAlerts, "pluginid"); let ignoredAlerts = []; if (isFilteredSite(newReportSite) && isFilteredSite(previousSite[0])) { ignoredAlerts = _.differenceBy(newReportSite.ignoredAlerts, previousSite[0].ignoredAlerts, "pluginid"); } else if (isFilteredSite(newReportSite)) { ignoredAlerts = newReportSite.ignoredAlerts; } removedAlerts = _.differenceBy(removedAlerts, ignoredAlerts, "pluginid"); newSite.alerts = newAlerts; newSite.removedAlerts = removedAlerts; newSite.ignoredAlerts = ignoredAlerts; siteClone.push(newSite); if (newAlerts.length !== 0 || removedAlerts.length !== 0 || ignoredAlerts.length !== 0) { newReport.updated = true; } } }); return siteClone; }, readMDFile: async (reportName) => { let res = ""; try { res = fs.readFileSync(reportName, { encoding: "base64" }); } catch (err) { console.error(`Error occurred while reading the markdown file! err: ${err.toString()}`); } return res; }, checkIfAlertsExists: (jsonReport) => { return jsonReport.site.some((s) => { return "alerts" in s && s.alerts.length !== 0; }); }, filterReport: async (jsonReport, plugins) => { jsonReport.site.forEach((s) => { if ("alerts" in s && s.alerts.length !== 0) { console.log(`starting to filter the alerts for site: ${s["@name"]}`); const newAlerts = s.alerts.filter(function (e) { return !plugins.includes(e.pluginid); }); const removedAlerts = s.alerts.filter(function (e) { return plugins.includes(e.pluginid); }); s.alerts = newAlerts; s.ignoredAlerts = removedAlerts; console.log(`#${newAlerts.length} alerts have been identified` + ` and #${removedAlerts.length} alerts have been ignored for the site.`); } }); return jsonReport; }, readPreviousReport: async (octokit, owner, repo, workSpace, runnerID, artifactName = "zap_scan") => { let previousReport; try { const artifactList = await octokit.request("GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts", { owner: owner, repo: repo, run_id: runnerID, }); const artifacts = artifactList.data.artifacts; let artifactID; if (artifacts.length !== 0) { artifacts.forEach((a) => { if (a.name === artifactName) { artifactID = a.id; } }); } if (artifactID !== undefined) { const download = await octokit.request("GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/zip", { owner: owner, repo: repo, artifact_id: artifactID, archive_format: "zip", }); const zip = new AdmZip(Buffer.from(download.data)); const zipEntries = zip.getEntries(); zipEntries.forEach(function (zipEntry) { if (zipEntry.entryName === "report_json.json") { previousReport = JSON.parse(zipEntry.getData().toString("utf8")); } }); } } catch (err) { console.error(`Error occurred while downloading the artifacts! err: ${err.toString()}`); } return previousReport; }, uploadArtifacts: async (rootDir, mdReport, jsonReport, htmlReport, artifactName = "zap_scan") => { const artifactClient = new DefaultArtifactClient(); const files = [ `${rootDir}/${mdReport}`, `${rootDir}/${jsonReport}`, `${rootDir}/${htmlReport}`, ]; const rootDirectory = rootDir; const options = {}; await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options); }, }; export default actionHelper;