Skip to content

Instantly share code, notes, and snippets.

@ggascoigne
Created January 14, 2020 21:37
Show Gist options
  • Save ggascoigne/fbe52ddf945813866bf0c9b20488b41d to your computer and use it in GitHub Desktop.
Save ggascoigne/fbe52ddf945813866bf0c9b20488b41d to your computer and use it in GitHub Desktop.
Allow exclusion of yarn audit failures.
// Run a vulnerability audit with some retry logic for if the audit
// service is temporarily down (which we have seen sometimes in jenkins builds)
/* eslint-disable @typescript-eslint/no-var-requires */
require('perish')
const execa = require('execa')
const path = require('path')
const fs = require('fs')
const promiseRetry = require('promise-retry')
const PROJECT_ROOT_DIRNAME = path.resolve(__dirname, '..')
// file created by running
// yarn audit --json --non-interactive > ./yarn-audit-known-issues
const KNOWN_ISSUES_FILE_NAME = './yarn-audit-known-issues'
const getAdvisoryId = item => parseInt(item.data.advisory.id)
const getAdvisories = input => {
const data = []
input.split('\n').forEach(line => {
if (line.length) {
const obj = JSON.parse(line)
if (obj.type === 'auditAdvisory') data.push(obj)
}
})
data.sort((item1, item2) => getAdvisoryId(item1) - getAdvisoryId(item2))
return data
}
const getKnownIssues = () => {
if (fs.existsSync(KNOWN_ISSUES_FILE_NAME)) {
const data = fs.readFileSync(KNOWN_ISSUES_FILE_NAME)
return getAdvisories(data.toString('utf8'))
}
}
const jsonEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b)
const dumpException = data => {
const advisory = data.data.advisory
console.table({
Severity: advisory.severity,
Title: advisory.title,
Package: advisory.module_name,
'Patched in':
advisory.patched_versions === '<0.0.0'
? 'No patch available'
: advisory.patched_versions,
'More info': advisory.url
})
}
const reportIssues = (known = [], current) => {
const exceptions = []
if (!known.length) {
current.forEach(c => exceptions.push(c))
} else {
for (let kIndex = 0, cIndex = 0; ; ) {
const k = known[kIndex]
const c = current[cIndex]
if (!k && !c) break
if (jsonEqual(k, c)) {
kIndex++
cIndex++
} else {
const kId = getAdvisoryId(k)
const cId = getAdvisoryId(c)
if (kId < cId) {
// known error no longer showing up
kIndex++
} else if (kId === cId) {
// the advisory has changed so we should log it
exceptions.push(c)
kIndex++
cIndex++
} else {
// new error, log it
exceptions.push(c)
cIndex++
}
}
}
}
if (exceptions.length) {
console.log('Audit Violations:')
exceptions.forEach(dumpException)
}
}
function runAudit() {
console.log('running vulnerability audit')
promiseRetry(async (retry, attempt) => {
const _ = await execa('yarn', ['audit', '--json', '--non-interactive'], {
cwd: PROJECT_ROOT_DIRNAME,
env: process.env,
all: true
}).catch(err => {
if (err.all.match(/503 Service Unavailable/)) {
console.log(
`audit service temporarily unavailable, retrying (attempt #${attempt})...`
)
retry()
}
const knownIssues = getKnownIssues()
const currentIssues = getAdvisories(err.stdout)
if (!jsonEqual(knownIssues, currentIssues)) {
reportIssues(knownIssues, currentIssues)
process.exit(1)
}
})
}).then()
}
runAudit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment