Skip to content

Commit

Permalink
Update enforce GHAS policy Action
Browse files Browse the repository at this point in the history
Signed-off-by: Brett Logan <lindluni@github.com>
  • Loading branch information
lindluni committed Oct 22, 2023
1 parent 0d8ec04 commit 171dc76
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 55 deletions.
7 changes: 4 additions & 3 deletions enforce-ghas-policy/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ inputs:
description: The slug of the organization
required: true
default: ${{ github.repository_owner }}
message_violation:
description: The message to use when CodeQL is missing
required: true
pull_request_number:
description: The ID of the pull request
required: true
Expand All @@ -21,6 +18,10 @@ inputs:
description: The slug of the repository
required: true
default: ${{ github.event.repository.name }}
threshold:
description: The minimum severity to fail the check
required: true
default: high
token:
description: The token to use for authentication
required: true
Expand Down
117 changes: 65 additions & 52 deletions enforce-ghas-policy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,20 @@ const {throttling} = require('@octokit/plugin-throttling')
const org = core.getInput('ORG', {required: true, trimWhitespace: true})
const repo = core.getInput('REPO', {required: true, trimWhitespace: true})
const ref = core.getInput('REF', {required: true, trimWhitespace: true})
const messageViolation = core.getInput('MESSAGE_VIOLATION', {required: true, trimWhitespace: true})
const pullRequestNumber = core.getInput('PULL_REQUEST_NUMBER', {required: true, trimWhitespace: true})
const threshold = core.getInput('THRESHOLD', {required: true, trimWhitespace: true}).toLowerCase()
const token = core.getInput('TOKEN', {required: true, trimWhitespace: true})

const thresholds = [
[{name: 'error'}],
[{name: 'note'}],
[{name: 'warning'}],
[{name: 'low'}],
[{name: 'medium'}],
[{name: 'high'}],
[{name: 'critical'}]
]

const _Octokit = Octokit.plugin(retry, throttling)
const client = new _Octokit({
auth: token,
Expand All @@ -28,75 +38,78 @@ const client = new _Octokit({

})

const calculateThresholds = (threshold) => {
let found = false
const validThresholds = []
for (const i in thresholds) {
if (found || thresholds[i].name === threshold) {
found = true
} else {
continue
}
validThresholds.push(thresholds[i])
}

return validThresholds
}

const comment = async (org, repo, number, message) => {
try {
core.info(`Commenting on PR #${number}`)
await client.issues.createComment({
owner: org,
repo: repo,
issue_number: number,
body: message
})
} catch (e) {
core.setFailed(`Error commenting on PR #${number}: ${e.message}`)
process.exit(0)
throw new Error(`Error commenting on PR #${number}: ${e.message}`)
}
}

const main = async () => {
try {
core.info('Checking if repository ignored')
await client.repos.getContent({
owner: org,
repo: repo,
path: '.github/.emass-repo-ignore'
})
core.info(`Repository is ignored, skipping CodeQL usage check`)
process.exit(0)
} catch (e) {
if (e.status !== 404) {
core.setFailed(`Error checking if repository is ignored: ${e.message}`)
process.exit(0)
}
const parseTotalPages = (link) => {
const regex = /page=(\d+)>; rel="last"/
const match = regex.exec(link)
if (match) {
return parseInt(match[1], 10)
}
return 0
}

const main = async () => {
try {
core.info(`Retrieving high severity CodeQL Code Scanning alerts for ${org}/${repo}/${ref}`)
const highAlerts = await client.paginate(client.codeScanning.listAlertsForRepo, {
owner: org,
repo: repo,
ref: ref,
severity: 'high',
state: 'open',
tool_name: 'CodeQL',
})

core.info(`Retrieving critical severity CodeQL Code Scanning alerts for ${org}/${repo}/${ref}`)
const criticalAlerts = await client.paginate(client.codeScanning.listAlertsForRepo, {
owner: org,
repo: repo,
ref: ref,
severity: 'high',
state: 'open',
tool_name: 'CodeQL',
})
core.info(`Found ${highAlerts.length} high and ${criticalAlerts.length} critical alerts`)
if (!thresholds[threshold]) {
return core.setFailed(`Invalid threshold [${threshold}], must be one of: ${Object.keys(thresholds).join(', ')}`)
}

if (highAlerts.length > 0 || criticalAlerts.length > 0) {
core.info(`Found ${highAlerts.length} high and ${criticalAlerts.length} critical alerts`)
const message = messageViolation.replace('{highAlerts}', String(highAlerts.length)).replace('{criticalAlerts}', String(criticalAlerts.length))
await comment(org, repo, pullRequestNumber, message)
core.setFailed(`GHAS security policy violation found. For additional information about OIS policy, please refer to the OIS SWA Wiki: https://department-of-veterans-affairs.github.io/ois-swa-wiki/docs/ghas/codeql-usage.`)
process.exit(1)
const findings = {}
const validThresholds = calculateThresholds(threshold)
for (const threshold of validThresholds) {
core.info(`Retrieving high severity CodeQL Code Scanning alerts for ${org}/${repo}/${ref}`)
const response = await client.codeScanning.listAlertsForRepo({
owner: org,
repo: repo,
ref: ref,
severity: threshold.name,
state: 'open',
tool_name: 'CodeQL',
per_page: 1
})
const totalPages = parseTotalPages(response.headers.link)
if (totalPages === 0 && response.data.length === 1 || totalPages > 0) {
findings[threshold.name] = totalPages === 0 ? 1 : totalPages
core.setFailed(`Found CodeQL Code Scanning alert of severity for ref ${ref} that exceed the ${threshold.name} threshold`)
} else {
findings[threshold.name] = 0
core.info(`No CodeQL Code Scanning alerts of severity ${threshold.name} for ref ${ref}`)
}
}

core.info(`Creating pull request comment for pull request #${pullRequestNumber}`)
const message = `### CodeQL Code Scanning Alerts\n\nYour pull request and repository violates the configured code scanning severity threshold(s) for the following severity(s):\n\n| Severity | Count |\n| --- | --- |\n${Object.keys(findings).map(key => `| ${key} | ${findings[key]} |`).join('\n')}\n\nPlease fix the issues and re-run the workflow.`
await comment(org, repo, pullRequestNumber, message)
} catch (e) {
core.setFailed(`Error checking for GHAS usage, please open a ticket here https://github.com/department-of-veterans-affairs/github-user-requests/issues/new/choose for additional help: ${e.message}`)
process.exit(0)
return core.setFailed(`Error validating CodeQL usage: ${e.message}`)
}
core.info(`GHAS security policy check complete`)
}

main().catch(e => {
core.setFailed(e.message)
process.exit(0)
})
main()

0 comments on commit 171dc76

Please sign in to comment.