From 34b30eebcc043d62ea5bd78e420aa79425302254 Mon Sep 17 00:00:00 2001 From: Andrea Brancaleoni Date: Thu, 21 Dec 2023 10:27:27 +0100 Subject: [PATCH] New workflow: dependabot-auto-dismiss --- .github/workflows/dependabot-auto-dismiss.yml | 35 +++++++++ src/dependabotDismiss.js | 74 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 .github/workflows/dependabot-auto-dismiss.yml create mode 100644 src/dependabotDismiss.js diff --git a/.github/workflows/dependabot-auto-dismiss.yml b/.github/workflows/dependabot-auto-dismiss.yml new file mode 100644 index 00000000..4e225a5d --- /dev/null +++ b/.github/workflows/dependabot-auto-dismiss.yml @@ -0,0 +1,35 @@ +name: Weekly Dependabot Auto Dismiss + +on: + schedule: + # Run at 9am UTC every Monday + - cron: "0 0 * * 1" + workflow_dispatch: + +jobs: + run: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 + with: + node-version: '20.x' + - id: npm + run: cd ${{ github.workspace }}; npm ci + shell: bash + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + SLACK_TOKEN: ${{ secrets.HOTSPOTS_SLACK_TOKEN }} + SLACK_CHANNEL: '#secops-hotspots' + DEBUG: false + with: + github-token: ${{ secrets.DEPENDABOT_AUTO_DISMISS_GITHUB_TOKEN }} + script: | + const debug = process.env.DEBUG === 'true'; + const { default: sendSlackMessage } = await import('${{ github.workspace }}/src/sendSlackMessage.js'); + const { default: dependabotDismiss } = await import('${{ github.workspace }}/src/dependabotDismiss.js'); + const message = await dependabotDismiss({debug, org: process.env.GITHUB_REPOSITORY_OWNER, github: github}); + if (message.length > 0) + await sendSlackMessage({debug, username: 'dependabot-auto-dismiss', message: message, channel: process.env.SLACK_CHANNEL, token: process.env.SLACK_TOKEN}); + + diff --git a/src/dependabotDismiss.js b/src/dependabotDismiss.js new file mode 100644 index 00000000..aa6136d5 --- /dev/null +++ b/src/dependabotDismiss.js @@ -0,0 +1,74 @@ +const Severity = { + low: 0, + medium: 1, + high: 2, + critical: 3, +} + +export default async function dependabotDismiss({ + org, + minlevel = Severity.low, + debug = false, + hotwords = [' dos ', 'denial of service', 'redos', 'denial-of-service', 'memory explosion'], + githubToken = null, + github = null, + actor = 'security-action', +}) { + let watermark = "The following alerts were dismissed because they contained hotwords:\n\n"; + let message = ''; + + if (!github && githubToken) { + const { Octokit } = await import("octokit"); + + github = new Octokit({ auth: githubToken }) + } + + if (!github && !githubToken) { + throw new Error('either githubToken or github is required!'); + } + + debug = debug === 'true' || debug === true; + + if (typeof severity === 'string') { + severity = Severity[severity]; + } + + const alerts = Array.from(await github.paginate('GET /orgs/{org}/dependabot/alerts', { + org: org, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + }, + sort: 'updated', + state: 'open', + severity: Object.keys(Severity).filter(s => Severity[s] >= minlevel) + })).filter(a => hotwords.some(h => a.security_advisory.summary.toLowerCase().includes(h))); + + for (const a of alerts) { + // get the first hotword that matches the summary + const hotword = hotwords.find(h => a.security_advisory.summary.toLowerCase().includes(h)).trim(); + + message += `- [${a.security_advisory.summary} in \`${org}/${a.repository.name}\`](${a.html_url})\n` + + if (debug) { + console.log(`Dismissing alert ${a.number} in ${a.repository.name} because it contains the hotword "${hotword}"`); + console.log(`Summary: ${a.security_advisory.summary}`); + continue; + } + + await github.request('PATCH /repos/{org}/{repo}/dependabot/alerts/{alert_number}', { + org: org, + repo: a.repository.name, + alert_number: a.number, + dismissed_reason: 'tolerable_risk', + dismissed_comment: `Dismissed by ${actor} because the alert summary contains the hotword "${hotword}"`, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + }, + state: 'dismissed', + }); + } + + if (message.length > 0) { + return watermark + message; + } +} \ No newline at end of file