From bf76bb7a1d88762307353dc623de86ab84eb1a34 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 26 Oct 2021 14:43:30 +0200 Subject: [PATCH] create github issue with a formatted body Co-authored-by: Harry Hogg Signed-off-by: Himanshu Mishra --- scripts/snyk-github-issue-sync.ts | 58 ++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts index 52e39da0cd..ed5082717d 100644 --- a/scripts/snyk-github-issue-sync.ts +++ b/scripts/snyk-github-issue-sync.ts @@ -15,15 +15,22 @@ */ // eslint-disable-next-line import/no-extraneous-dependencies import { Octokit } from '@octokit/rest'; -// The GitHub workflow .github/workflows/ -import syncJsonOutput from '../snyk.json'; +// Generated by GitHub workflow .github/workflows/snyk-github-issue-creator +import synkJsonOutput from '../snyk.json'; + +// Pattern for a GitHub Issue title +// Snyk vulnerability [Vulnerability ID] type Vulnerability = { description: string; - id: string; + snykId: string; packages: Set; }; +// Remember to fix me! +const GH_OWNER = 'orkohunter'; +const GH_REPO = 'backstage'; + const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN, }); @@ -32,14 +39,16 @@ const fetchSnykGithubIssueMap = async (): Promise> => { const snykGithubIssueMap: Record = {}; const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, { - owner: 'backstage', - repo: 'backstage', + // TODO(Harry/Himanshu): Use a CLI flag for these values. + owner: GH_OWNER, + repo: GH_REPO, per_page: 100, - labels: 'snyk', + labels: 'snyk-vulnerability', }); for await (const { data: issues } of iterator) { for (const issue of issues) { + // Gets the Vulnerability ID from square braces const match = /\([([A-Z0-9-]+)\])/.exec(issue.title); if (match && match[1]) { @@ -51,13 +60,31 @@ const fetchSnykGithubIssueMap = async (): Promise> => { return snykGithubIssueMap; }; +const generateIssueBody = (vulnerability: Vulnerability) => { + let issueBody = ''; + issueBody += '## Affecting Packages/Plugins\n'; + vulnerability.packages.forEach(pkgName => { + issueBody += `* ${pkgName}\n`; + }); + // TODO: Use displayTargetFile in snyk.json to create hyperlinks + issueBody += '\n'; + issueBody += vulnerability.description; + return issueBody; +}; + const createGithubIssue = (vulnerability: Vulnerability) => { console.log( `Create issue for vulnerability ${ - vulnerability.id + vulnerability.snykId } affecting packages ${Array.from(vulnerability.packages)}`, ); - // TODO(hhogg): Create github issue with the contents from a Snyk issue. + octokit.issues.create({ + owner: GH_OWNER, + repo: GH_REPO, + title: `Snyk vulnerability [${vulnerability.snykId}]`, + labels: ['snyk-vulnerability', 'help wanted'], + body: generateIssueBody(vulnerability), + }); }; const updateGithubIssue = ( @@ -65,7 +92,7 @@ const updateGithubIssue = ( vulnerability: Vulnerability, ) => { console.log( - `Update issue ${githubIssueId} for vulnerability ${vulnerability.id}`, + `Update issue ${githubIssueId} for vulnerability ${vulnerability.snykId}`, ); // TODO(hhogg): Update github issue with the contents from a Snyk issue. }; @@ -75,12 +102,12 @@ const closeGithubIssue = (githubIssueId: number) => { // TODO(hhogg): Delete a github issue }; -(async () => { +async function main() { const snykGithubIssueMap = await fetchSnykGithubIssueMap(); const vulnerabilityStore: Record = {}; // Group the Snyk vulnerabilities, and aggregate the affecting packages. - syncJsonOutput.forEach(({ projectName, vulnerabilities }) => { + synkJsonOutput.forEach(({ projectName, vulnerabilities }) => { vulnerabilities.forEach( ({ id, description }: { id: string; description: string }) => { if (id !== undefined && description !== undefined) { @@ -89,7 +116,7 @@ const closeGithubIssue = (githubIssueId: number) => { } else { vulnerabilityStore[id] = { description, - id, + snykId: id, packages: new Set([projectName]), }; } @@ -113,4 +140,9 @@ const closeGithubIssue = (githubIssueId: number) => { closeGithubIssue(githubIssueId); } }); -})(); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +});