From 3fcd967c4e3f47db7b04bc54efc7682995a82fdb Mon Sep 17 00:00:00 2001 From: Popov Aleksey Date: Fri, 28 Jul 2023 17:48:11 +0300 Subject: [PATCH] build: added Check security alerts workflow for all repositories --- workflows/check-security-alerts.yml | 107 ++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 workflows/check-security-alerts.yml diff --git a/workflows/check-security-alerts.yml b/workflows/check-security-alerts.yml new file mode 100644 index 0000000..9014e38 --- /dev/null +++ b/workflows/check-security-alerts.yml @@ -0,0 +1,107 @@ +name: Check security alerts + +on: + schedule: + - cron: "30 1 * * *" + workflow_dispatch: + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v6 + with: + github-token: ${{ secrets.ACTIVE_TOKEN }} + script: | + if (!'${{secrets.SECURITY_ISSUE_REPO}}') + return; + + const { owner, repo } = context.repo; + const state = 'open'; + const dependabotLabel = 'dependabot'; + const codeqlLabel = 'codeql'; + const securityLabel = 'security'; + + async function getDependabotAlerts () { + const dependabotListAlertsUrl = `https://api.github.com/repos/${ owner }/${ repo }/dependabot/alerts?state=${ state }`; + const dependabotRequestOptions = { + headers: { 'Authorization': 'Bearer ${{ secrets.ACTIVE_TOKEN }}' } + } + + const response = await fetch(dependabotListAlertsUrl, dependabotRequestOptions); + const data = await response.json(); + + // If data isn't arry somethig goes wrong + if (Array.isArray(data)) + return data; + + return []; + } + + async function getCodeqlAlerts () { + // When CodeQL is turned of it throws error + try { + const { data } = await github.rest.codeScanning.listAlertsForRepo({ owner, repo, state }); + + return data; + } catch (_) { + return []; + } + } + + async function createIssue ({owner, repo, labels, summary, description, link, package = ''}) { + const title = `[${repo}] ${summary}`; + const body = '' + + `#### Repository: \`${ repo }\`\n` + + (!!package ? `#### Package: \`${ package }\`\n` : '') + + `#### Description:\n` + + `${ description }\n` + + `#### Link: ${ link }` + + return github.rest.issues.create({ owner, repo, title, body, labels }); + } + + function needCreateIssue (alert) { + return !issueDictionary[alert.html_url] + && Date.now() - new Date(alert.updated_at) <= 1000 * 60 * 60 * 24; + } + + const dependabotAlerts = await getDependabotAlerts(); + const codeqlAlerts = await getCodeqlAlerts(); + const {data: existedIssues} = await github.rest.issues.listForRepo({ owner, repo, labels: [securityLabel], state }); + + const issueDictionary = existedIssues.reduce((res, issue) => { + const alertUrl = issue.body.match(/Link:\s*(https.*\d*)/)?.[1]; + + if (alertUrl) + res[alertUrl] = issue; + + return res; + }, {}) + + dependabotAlerts.forEach(alert => { + if (!needCreateIssue(alert)) + return; + + createIssue({ owner, + repo: '${{ secrets.SECURITY_ISSUE_REPO }}', + labels: [dependabotLabel, securityLabel], + summary: alert.security_advisory.summary, + description: alert.security_advisory.description, + link: alert.html_url, + package: alert.dependency.package.name + }) + }); + + codeqlAlerts.forEach(alert => { + if (!needCreateIssue(alert)) + return; + + createIssue({ owner, + repo: '${{ secrets.SECURITY_ISSUE_REPO }}', + labels: [codeqlLabel, securityLabel], + summary: alert.rule.description, + description: alert.most_recent_instance.message.text, + link: alert.html_url, + }) + }); \ No newline at end of file