1+ name : Check security alerts
2+
3+ on :
4+ schedule :
5+ - cron : " 30 1 * * *"
6+ workflow_dispatch :
7+
8+ jobs :
9+ check :
10+ runs-on : ubuntu-latest
11+ steps :
12+ - uses : actions/github-script@v6
13+ with :
14+ github-token : ${{ secrets.ACTIVE_TOKEN }}
15+ script : |
16+ if (!'${{secrets.SECURITY_ISSUE_REPO}}')
17+ return;
18+
19+ const { owner, repo } = context.repo;
20+ const state = 'open';
21+ const dependabotLabel = 'dependabot';
22+ const codeqlLabel = 'codeql';
23+ const securityLabel = 'security';
24+
25+ async function getDependabotAlerts () {
26+ const dependabotListAlertsUrl = `https://api.github.com/repos/${ owner }/${ repo }/dependabot/alerts?state=${ state }`;
27+ const dependabotRequestOptions = {
28+ headers: { 'Authorization': 'Bearer ${{ secrets.ACTIVE_TOKEN }}' }
29+ }
30+
31+ const response = await fetch(dependabotListAlertsUrl, dependabotRequestOptions);
32+ const data = await response.json();
33+
34+ // If data isn't arry somethig goes wrong
35+ if (Array.isArray(data))
36+ return data;
37+
38+ return [];
39+ }
40+
41+ async function getCodeqlAlerts () {
42+ // When CodeQL is turned of it throws error
43+ try {
44+ const { data } = await github.rest.codeScanning.listAlertsForRepo({ owner, repo, state });
45+
46+ return data;
47+ } catch (_) {
48+ return [];
49+ }
50+ }
51+
52+ async function createIssue ({owner, repo, labels, summary, description, link, package = ''}) {
53+ const title = `[${repo}] ${summary}`;
54+ const body = ''
55+ + `#### Repository: \`${ repo }\`\n`
56+ + (!!package ? `#### Package: \`${ package }\`\n` : '')
57+ + `#### Description:\n`
58+ + `${ description }\n`
59+ + `#### Link: ${ link }`
60+
61+ return github.rest.issues.create({ owner, repo, title, body, labels });
62+ }
63+
64+ function needCreateIssue (alert) {
65+ return !issueDictionary[alert.html_url]
66+ && Date.now() - new Date(alert.updated_at) <= 1000 * 60 * 60 * 24;
67+ }
68+
69+ const dependabotAlerts = await getDependabotAlerts();
70+ const codeqlAlerts = await getCodeqlAlerts();
71+ const {data: existedIssues} = await github.rest.issues.listForRepo({ owner, repo, labels: [securityLabel], state });
72+
73+ const issueDictionary = existedIssues.reduce((res, issue) => {
74+ const alertUrl = issue.body.match(/Link:\s*(https.*\d*)/)?.[1];
75+
76+ if (alertUrl)
77+ res[alertUrl] = issue;
78+
79+ return res;
80+ }, {})
81+
82+ dependabotAlerts.forEach(alert => {
83+ if (!needCreateIssue(alert))
84+ return;
85+
86+ createIssue({ owner,
87+ repo: '${{ secrets.SECURITY_ISSUE_REPO }}',
88+ labels: [dependabotLabel, securityLabel],
89+ summary: alert.security_advisory.summary,
90+ description: alert.security_advisory.description,
91+ link: alert.html_url,
92+ package: alert.dependency.package.name
93+ })
94+ });
95+
96+ codeqlAlerts.forEach(alert => {
97+ if (!needCreateIssue(alert))
98+ return;
99+
100+ createIssue({ owner,
101+ repo: '${{ secrets.SECURITY_ISSUE_REPO }}',
102+ labels: [codeqlLabel, securityLabel],
103+ summary: alert.rule.description,
104+ description: alert.most_recent_instance.message.text,
105+ link: alert.html_url,
106+ })
107+ });
0 commit comments