Using graphql to pull alert details

closes #84
closes #102
This commit is contained in:
Michael Waddell
2022-02-17 07:45:14 -06:00
parent 37d2f784cd
commit d5d6d4da96
4 changed files with 77 additions and 18 deletions

View File

@@ -1,15 +1,30 @@
import * as YAML from 'yaml'
export interface updatedDependency {
export interface dependencyAlert {
alertState: string,
ghsaId: string,
cvss: number
}
export interface updatedDependency extends dependencyAlert {
dependencyName: string,
dependencyType: string,
updateType: string,
directory: string,
packageEcosystem: string,
targetBranch: string
targetBranch: string,
prevVersion: string,
newVersion: string
}
export function parse (commitMessage: string, branchName: string, mainBranch: string): Array<updatedDependency> {
export interface alertLookup {
(dependencyName: string, dependencyVersion: string, directory: string): Promise<dependencyAlert>;
}
export async function parse (commitMessage: string, branchName: string, mainBranch: string, lookup: alertLookup): Promise<Array<updatedDependency>> {
const firstLine = commitMessage.split('\n')[0]
const directory = firstLine.match(/ in (?<directory>\/[^ ]*)$/)
const bumpFragment = commitMessage.match(/^Bumps .* from (?<from>\d[^ ]*) to (?<to>\d[^ ]*)\.$/m)
const yamlFragment = commitMessage.match(/^-{3}\n(?<dependencies>[\S|\s]*?)\n^\.{3}\n/m)
if (yamlFragment?.groups && branchName.startsWith('dependabot')) {
@@ -18,21 +33,24 @@ export function parse (commitMessage: string, branchName: string, mainBranch: st
// Since we are on the `dependabot` branch (9 letters), the 10th letter in the branch name is the delimiter
const delim = branchName[10]
const chunks = branchName.split(delim)
const dirname = chunks.slice(2, -1).join(delim) || '/'
const dirname = directory?.groups?.directory ?? "/"
const prev = bumpFragment?.groups?.from ?? ""
const next = bumpFragment?.groups?.to ?? ""
if (data['updated-dependencies']) {
return data['updated-dependencies'].map(dependency => {
return {
dependencyName: dependency['dependency-name'],
dependencyType: dependency['dependency-type'],
updateType: dependency['update-type'],
directory: dirname,
packageEcosystem: chunks[1],
targetBranch: mainBranch
}
})
return await Promise.all(data['updated-dependencies'].map(async (dependency) => ({
dependencyName: dependency['dependency-name'],
dependencyType: dependency['dependency-type'],
updateType: dependency['update-type'],
directory: dirname,
packageEcosystem: chunks[1],
targetBranch: mainBranch,
prevVersion: prev,
newVersion: next,
...await lookup(dependency['dependency-name'], prev, dirname)
})))
}
}
return []
return Promise.resolve([])
}

View File

@@ -1,6 +1,7 @@
import * as core from '@actions/core'
import { GitHub } from '@actions/github/lib/utils'
import { Context } from '@actions/github/lib/context'
import type { dependencyAlert } from './update_metadata'
const DEPENDABOT_LOGIN = 'dependabot[bot]'
@@ -61,3 +62,41 @@ function warnOtherCommits (): void {
'any non-Dependabot changes.'
)
}
export async function getAlert (name: string, version: string, directory: string, client: InstanceType<typeof GitHub>, context: Context): Promise<dependencyAlert> {
const alerts: any = await client.graphql(`
{
repository(owner: "${context.repo.owner}", name: "${context.repo.repo}") {
vulnerabilityAlerts(first: 100) {
nodes {
vulnerableManifestFilename
vulnerableManifestPath
vulnerableRequirements
state
securityVulnerability {
package { name }
}
securityAdvisory {
cvss { score }
ghsaId
}
}
}
}
}`)
const nodes = alerts?.repository?.vulnerabilityAlerts?.nodes
const found = nodes.find(a => a.vulnerableRequirements == `= ${version}`
&& trimSlashes(a.vulnerableManifestPath) == `${trimSlashes(directory)}/${a.vulnerableManifestFilename}`
&& a.securityVulnerability.package.name == name)
return {
alertState: found?.state ?? "",
ghsaId: found?.securityAdvisory.ghsaId ?? "",
cvss: found?.securityAdvisory.cvss.score ?? 0.0
}
}
export function trimSlashes(value: string): string {
return value.replace(/^\//, '').replace(/\/$/, '')
}

View File

@@ -5,7 +5,7 @@ import * as dotenv from 'dotenv'
import { Argv } from 'yargs'
import { hideBin } from 'yargs/helpers'
import { getMessage } from './dependabot/verified_commits'
import { getMessage, getAlert } from './dependabot/verified_commits'
import { parse } from './dependabot/update_metadata'
import { getBranchNames, parseNwo } from './dependabot/util'
@@ -50,8 +50,9 @@ async function check (args: any): Promise<void> {
if (commitMessage) {
console.log('This appears to be a valid Dependabot Pull Request.')
const branchNames = getBranchNames(newContext)
const alertLookup = (name, version, directory) => getAlert(name, version, directory, githubClient, actionContext)
const updatedDependencies = parse(commitMessage, branchNames.headName, branchNames.baseName)
const updatedDependencies = await parse(commitMessage, branchNames.headName, branchNames.baseName, alertLookup)
if (updatedDependencies.length > 0) {
console.log('Updated dependencies:')

View File

@@ -24,12 +24,13 @@ export async function run (): Promise<void> {
// Validate the job
const commitMessage = await verifiedCommits.getMessage(githubClient, github.context)
const branchNames = util.getBranchNames(github.context)
const alertLookup = (name, version, directory) => verifiedCommits.getAlert(name, version, directory, githubClient, github.context)
if (commitMessage) {
// Parse metadata
core.info('Parsing Dependabot metadata')
const updatedDependencies = updateMetadata.parse(commitMessage, branchNames.headName, branchNames.baseName)
const updatedDependencies = await updateMetadata.parse(commitMessage, branchNames.headName, branchNames.baseName, alertLookup)
if (updatedDependencies.length > 0) {
output.set(updatedDependencies)