Skip to content

Commit ca90b47

Browse files
author
testcafe-build-bot
committed
🔄 created local '.github/scripts/security-checker.mjs' from remote 'scripts/security-checker.mjs'
1 parent a3b4a54 commit ca90b47

1 file changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
const STATES = {
2+
open: 'open',
3+
closed: 'closed',
4+
};
5+
6+
const LABELS = {
7+
dependabot: 'dependabot',
8+
codeq: 'codeql',
9+
security: 'security notification',
10+
};
11+
12+
class SecurityChecker {
13+
constructor (github, context, issueRepo) {
14+
this.github = github;
15+
this.issueRepo = issueRepo;
16+
this.context = {
17+
owner: context.repo.owner,
18+
repo: context.repo.repo,
19+
};
20+
}
21+
22+
async check () {
23+
const dependabotAlerts = await this.getDependabotAlerts();
24+
const codeqlAlerts = await this.getCodeqlAlerts();
25+
const existedIssues = await this.getExistedIssues();
26+
27+
this.alertDictionary = this.createAlertDictionary(existedIssues);
28+
29+
await this.closeSpoiledIssues();
30+
this.createDependabotlIssues(dependabotAlerts);
31+
this.createCodeqlIssues(codeqlAlerts);
32+
}
33+
34+
async getDependabotAlerts () {
35+
const { data } = await this.github.rest.dependabot.listAlertsForRepo({ state: STATES.open, ...this.context });
36+
37+
return data;
38+
}
39+
40+
async getCodeqlAlerts () {
41+
try {
42+
const { data } = await this.github.rest.codeScanning.listAlertsForRepo({ state: STATES.open, ...this.context });
43+
44+
return data;
45+
}
46+
catch (e) {
47+
if (e.message.includes('no analysis found'))
48+
return [];
49+
50+
throw e;
51+
}
52+
}
53+
54+
async getExistedIssues () {
55+
const { data: existedIssues } = await this.github.rest.issues.listForRepo({
56+
owner: this.context.owner,
57+
repo: this.issueRepo,
58+
labels: [LABELS.security],
59+
state: STATES.open,
60+
});
61+
62+
return existedIssues;
63+
}
64+
65+
createAlertDictionary (existedIssues) {
66+
return existedIssues.reduce((res, issue) => {
67+
const [, url, number] = issue.body.match(/Link:\s*(https.*?(\d+)$)/);
68+
69+
if (!url)
70+
return res;
71+
72+
res[url] = {
73+
issue, number,
74+
isDependabot: url.includes('dependabot'),
75+
};
76+
77+
return res;
78+
}, {});
79+
}
80+
81+
async closeSpoiledIssues () {
82+
for (const key in this.alertDictionary) {
83+
const alert = this.alertDictionary[key];
84+
85+
if (alert.isDependabot) {
86+
const isAlertOpened = await this.isDependabotAlertOpened(alert.number);
87+
88+
if (isAlertOpened)
89+
continue;
90+
91+
await this.closeIssue(alert.issue.number);
92+
}
93+
}
94+
}
95+
96+
async isDependabotAlertOpened (alertNumber) {
97+
const alert = await this.getDependabotAlertInfo(alertNumber);
98+
99+
return alert.state === STATES.open;
100+
}
101+
102+
async getDependabotAlertInfo (alertNumber) {
103+
try {
104+
const { data } = await this.github.rest.dependabot.getAlert({ alert_number: alertNumber, ...this.context });
105+
106+
return data;
107+
}
108+
catch (e) {
109+
if (e.message.includes('No alert found for alert number'))
110+
return {};
111+
112+
throw e;
113+
}
114+
}
115+
116+
async closeIssue (issueNumber) {
117+
return this.github.rest.issues.update({
118+
owner: this.context.owner,
119+
repo: this.issueRepo,
120+
issue_number: issueNumber,
121+
state: STATES.closed,
122+
});
123+
}
124+
125+
async createDependabotlIssues (dependabotAlerts) {
126+
dependabotAlerts.forEach(alert => {
127+
if (!this.needCreateIssue(alert))
128+
return;
129+
130+
this.createIssue({
131+
labels: [LABELS.dependabot, LABELS.security, alert.dependency.scope],
132+
originRepo: this.context.repo,
133+
summary: alert.security_advisory.summary,
134+
description: alert.security_advisory.description,
135+
link: alert.html_url,
136+
issuePackage: alert.dependency.package.name,
137+
});
138+
});
139+
}
140+
141+
async createCodeqlIssues (codeqlAlerts) {
142+
codeqlAlerts.forEach(alert => {
143+
if (!this.needCreateIssue(alert))
144+
return;
145+
146+
this.createIssue({
147+
labels: [LABELS.codeql, LABELS.security],
148+
originRepo: this.context.repo,
149+
summary: alert.rule.description,
150+
description: alert.most_recent_instance.message.text,
151+
link: alert.html_url,
152+
});
153+
});
154+
}
155+
156+
needCreateIssue (alert) {
157+
return !this.alertDictionary[alert.html_url];
158+
}
159+
160+
async createIssue ({ labels, originRepo, summary, description, link, issuePackage = '' }) {
161+
const title = `[${originRepo}] ${summary}`;
162+
const body = ''
163+
+ `#### Repository: \`${originRepo}\`\n`
164+
+ (issuePackage ? `#### Package: \`${issuePackage}\`\n` : '')
165+
+ `#### Description:\n`
166+
+ `${description}\n`
167+
+ `#### Link: ${link}`;
168+
169+
return this.github.rest.issues.create({
170+
title, body, labels,
171+
owner: this.context.owner,
172+
repo: this.issueRepo,
173+
});
174+
}
175+
}
176+
177+
export default SecurityChecker;

0 commit comments

Comments
 (0)