Skip to content

Commit 239486f

Browse files
authored
Merge pull request #107 from github/xavier-patch
Check for duplicates
2 parents bf089a3 + f7728bd commit 239486f

6 files changed

Lines changed: 100 additions & 25 deletions

File tree

.github/actions/replicate/__tests__/replicate.test.js

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/actions/replicate/__tests__/replicate.test.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as core from '@actions/core'
22
import * as replicate from '../replicate'
3+
import * as issues from '../issues'
34
import { WebhookPayload, PayloadRepository } from '@actions/github/lib/interfaces'
45

56
const TEST_ISSUE_1 = 1
@@ -71,8 +72,9 @@ This is the issue body second line
7172
}
7273

7374
const TEST_GENERATED_ISSUE: replicate.Issue = {
74-
title: '[BOUNTY - All For One] Issue Title',
75+
title: '[All For One] Issue Title',
7576
labels: ['All For One','not-a-bounty-label'],
77+
bountyType: 'All For One',
7678
body: `Original external [issue](https://github.com/test_owner/test_repo/issues/1)
7779
7880
Sumitted by [ghsecuritylab](https://github.com/ghsecuritylab)
@@ -118,3 +120,20 @@ describe('generates proper content', () => {
118120
})
119121
})
120122

123+
describe('check for duplicates', () => {
124+
it('can find duplicates', async () => {
125+
const TEST_REF: number = 31
126+
const TEST_BODY1 = `Original external [issue](https://github.com/owner/repo/issues/1)\n\nThen there is some text`
127+
const TEST_BODY2 = `Original external [issue](https://github.com/owner/repo/issues/2)\n\nThen there is some text`
128+
const TEST_INTERNAL_ISSUES: issues.Issue_info[] = [
129+
{title: 'issue 1', author: 'author1', body: TEST_BODY1, number: 31},
130+
{title: 'issue 2', author: 'author2', body: TEST_BODY2, number: 33}
131+
]
132+
let foundRef: number | undefined = issues.internalIssueAlreadyCreated('https://github.com/owner/repo/issues/1', TEST_INTERNAL_ISSUES)
133+
expect(foundRef).toEqual(TEST_REF)
134+
foundRef = issues.internalIssueAlreadyCreated('https://github.com/owner/repo/issues/3', TEST_INTERNAL_ISSUES)
135+
expect(foundRef).toBeUndefined()
136+
})
137+
})
138+
139+

.github/actions/replicate/issues.js

Lines changed: 10 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/actions/replicate/issues.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as replicate from './replicate'
55
export type Issue_info = {title: string, author: string, body: string, number: number}
66
type Issue_state = 'open' | 'all' | 'closed' | undefined
77

8-
export const getIssueList = async (owner: string, repo: string, token: string | undefined, open: boolean) : Promise<Issue_info[] | undefined> => {
8+
export const getIssueList = async (owner: string, repo: string, token: string | undefined, open: boolean, checkBountyLabels: boolean, per_page?: number) : Promise<Issue_info[] | undefined> => {
99
if(!token) {
1010
core.debug("No valid token for creating issues on the internal repo")
1111
return
@@ -19,14 +19,15 @@ export const getIssueList = async (owner: string, repo: string, token: string |
1919
owner,
2020
repo,
2121
state: issueState,
22+
per_page: per_page? per_page : 100 // TODO: implement proper pagination
2223
// labels: labelFilter -- Does not work properly
2324
})
2425

2526
issues.data.forEach(issue => {
26-
const bountyLabel = issue.labels.some(label => {
27-
return replicate.BOUNTY_LABELS.includes(label.name)
28-
})
29-
if(bountyLabel){
27+
const bountyLabel = checkBountyLabels? issue.labels.some(label => {
28+
return replicate.BOUNTY_LABELS.includes(label.name as replicate.BountyType)
29+
}) : undefined
30+
if(!checkBountyLabels || bountyLabel){
3031
let item: Issue_info = {
3132
title: issue.title,
3233
author: issue.user?.login,
@@ -52,8 +53,12 @@ export const isUserAlreadyParticipant = (user: string, externalSubmissions: Issu
5253
return check
5354
}
5455

56+
function escapeRegExp(text: string) {
57+
return text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
58+
}
59+
5560
export const internalIssueAlreadyCreated = (externalSubmissionUrl: string | undefined, internalIssues: Issue_info[]) : number | undefined => {
56-
const searchString = `/Original external [issue](${externalSubmissionUrl})/`
61+
const searchString = new RegExp(escapeRegExp(`Original external [issue](${externalSubmissionUrl})`))
5762
let ref: number | undefined = undefined
5863
internalIssues.some( element => {
5964
if(element.body.search(searchString) != -1) {

.github/actions/replicate/replicate.js

Lines changed: 17 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/actions/replicate/replicate.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import * as github from '@actions/github'
33
import { WebhookPayload } from '@actions/github/lib/interfaces'
44
import { getIssueList, internalIssueAlreadyCreated, isUserAlreadyParticipant } from './issues'
55

6-
export type Issue = {title: string, body: string, labels: string[]}
7-
export const BOUNTY_LABELS: string[] = ['All For One', 'The Bug Slayer']
8-
const COMMENT_TASK_LIST = `## Task List
6+
export const BOUNTY_LABELS = ['All For One', 'The Bug Slayer'] as const
7+
export type BountyType = typeof BOUNTY_LABELS[number]
8+
type CommentMap = {[K in BountyType]: string}
9+
export type Issue = {title: string, body: string, labels: string[], bountyType: BountyType}
10+
11+
const COMMENT_TASK_LIST_AFO = `## Task List
912
- [ ] Initial assessment - Please record your decision in the comment below
1013
- [ ] CodeQL
1114
- [ ] Security Lab
@@ -21,6 +24,20 @@ const COMMENT_TASK_LIST = `## Task List
2124
- [ ] Bounty Payment
2225
`
2326

27+
const COMMENT_TASK_LIST_BS = `## Task List
28+
- [ ] Initial assessment from Security Lab
29+
- [ ] Security Lab assessment:
30+
- [ ] Confirm the CVE
31+
- [ ] Assess the Vulnerability Impact, the Vulnerability Scope
32+
- [ ] Get the CodeQL scores (False Positive ratio, Code Maturity and the Documentation) from the previous query rating
33+
- [ ] PR is merged? Finalize the score
34+
- [ ] Bounty Payment`
35+
36+
const COMMENT_TASK_LIST: CommentMap = {
37+
'All For One': COMMENT_TASK_LIST_AFO,
38+
'The Bug Slayer': COMMENT_TASK_LIST_BS
39+
}
40+
2441
const COMMENT_SCORING = `## Scoring
2542
| Criterion | Score|
2643
|--- | --- |
@@ -40,7 +57,7 @@ const COMMENT_FIRST_SUBMISSION = `## :tada: First submission for this user :tada
4057

4158
export const generateInternalIssueContentFromPayload = async (payload: WebhookPayload): Promise<Issue | undefined> => {
4259
const issue = payload.issue
43-
let result: Issue = {title: "none", body: "none", labels: []}
60+
let result: Issue = {title: 'none', body: 'none', labels: [], bountyType: 'All For One'}
4461
let bountyIssue: boolean = false
4562
let bountyType = ''
4663

@@ -103,7 +120,7 @@ export const createInternalIssue = async (payload: WebhookPayload, issue: Issue)
103120
owner,
104121
repo,
105122
issue_number: internal_ref,
106-
body: COMMENT_TASK_LIST,
123+
body: COMMENT_TASK_LIST[issue.bountyType],
107124
})
108125
core.debug(`comment created ${issueCommentResponse1.data.url}`)
109126

@@ -162,7 +179,7 @@ const checkDuplicates = async (payload: WebhookPayload): Promise<boolean> => {
162179
const internalRepoAccessToken: string | undefined = process.env['INT_REPO_TOKEN']
163180
const internalRepo = core.getInput('internal_repo') || '/'
164181
const [owner, repo] = internalRepo.split('/')
165-
const internalIssues = await getIssueList(owner, repo, internalRepoAccessToken, false)
182+
const internalIssues = await getIssueList(owner, repo, internalRepoAccessToken, false, false)
166183
if(!internalIssues) {
167184
core.debug('Internal error. Cannot check for duplicates. Aborting')
168185
return true
@@ -180,7 +197,7 @@ export const isFirstSubmission = async (payload: WebhookPayload, token : string
180197
const repository = payload.repository
181198
if(!repository)
182199
return false
183-
const allSubmissions = await getIssueList(repository.owner.login, repository.name, token, false)
200+
const allSubmissions = await getIssueList(repository.owner.login, repository.name, token, false, true)
184201
return !isUserAlreadyParticipant(payload.issue?.user.login, allSubmissions)
185202
}
186203

0 commit comments

Comments
 (0)