Skip to content

Commit

Permalink
Merge 25bb15d into 1390024
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacs committed May 1, 2020
2 parents 1390024 + 25bb15d commit b3d5fcc
Show file tree
Hide file tree
Showing 59 changed files with 8,867 additions and 8,597 deletions.
67 changes: 4 additions & 63 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,4 @@
---
################################################################################
# Template - Node CI
#
# Description:
# This contains the basic information to: install dependencies, run tests,
# get coverage, and run linting on a nodejs project. This template will run
# over the MxN matrix of all operating systems, and all current LTS versions
# of NodeJS.
#
# Dependencies:
# This template assumes that your project is using the `tap` module for
# testing. If you're not using this module, then the step that runs your
# coverage will need to be adjusted.
#
################################################################################
name: Node CI

on: [push, pull_request]
Expand All @@ -23,72 +8,28 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [10.x, 12.x, 13.x]
node-version: [10.x, 12.x, 14.x]
os: [ubuntu-latest, windows-latest, macOS-latest]

runs-on: ${{ matrix.os }}

steps:
# Checkout the repository
- uses: actions/checkout@v2
# Installs the specific version of Node.js
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

################################################################################
# Install Dependencies
#
# ASSUMPTIONS:
# - The project has a package-lock.json file
#
# Simply run the tests for the project.
################################################################################
- name: Install dependencies
run: npm ci

################################################################################
# Run Testing
#
# ASSUMPTIONS:
# - The project has `tap` as a devDependency
# - There is a script called "test" in the package.json
#
# Simply run the tests for the project.
################################################################################
- name: Run tests
if: matrix.node-version != '14.x' || matrix.os != 'ubuntu-latest'
run: npm test

################################################################################
# Run coverage check
#
# ASSUMPTIONS:
# - The project has `tap` as a devDependency
# - There is a script called "coverage" in the package.json
#
# Coverage should only be posted once, we are choosing the latest LTS of
# node, and ubuntu as the matrix point to post coverage from. We limit
# to the 'push' event so that coverage ins't posted twice from the
# pull-request event, and push event (line 3).
################################################################################
- name: Run coverage report
if: github.event_name == 'push' && matrix.node-version == '12.x' && matrix.os == 'ubuntu-latest'
run: npm run coverage
if: matrix.node-version == '14.x' && matrix.os == 'ubuntu-latest'
run: npm test
env:
# The environment variable name is leveraged by `tap`
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

################################################################################
# Run linting
#
# ASSUMPTIONS:
# - There is a script called "lint" in the package.json
#
# We run linting AFTER we run testing and coverage checks, because if a step
# fails in an GitHub Action, all other steps are not run. We don't want to
# fail to run tests or coverage because of linting. It should be the lowest
# priority of all the steps.
################################################################################
- name: Run linter
run: npm run lint
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
/node_modules
/coverage
.vscode
.nyc_output
/.nyc_output
.DS_Store
.idea
npm-audit-report.iml
npm-audit-report.iml
42 changes: 35 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,52 @@ The response is an object that contains an output string (the report) and a sugg

## Basic usage example

This is intended to be used along with
[`@npmcli/arborist`](http://npm.im/@npmcli/arborist)'s `AuditReport` class.

```
'use strict'
const Report = require('npm-audit-report')
const options = {
reporter: 'json'
}
Report(response, options, (result) => {
console.log(result.report)
const arb = new Arborist({ path: '/path/to/project' })
arb.audit().then(report => {
const result = new Report(report, options)
console.log(result.output)
process.exitCode = result.exitCode
})
```

## Break from Version 1

Version 5 and 6 of the npm CLI make a request to the registry endpoint at
either the "Full Audit" endpoint at `/-/npm/v1/security/audits` or
the "Quick Audit" endpoint at `/-/npm/v1/security/audits/quick`. The Full
Audit endpoint calculates remediations necessary to correct problems based
on the shape of the tree.

As of npm v7, the logic of how the cli manages trees is dramatically
rearchitected, rendering much of the remediations no longer valid.
Thus, it _only_ fetches the advisory data from the Quick Audit endpoint,
and uses [`@npmcli/arborist`](http://npm.im/@npmcli/arborist) to calculate
required remediations and affected nodes in the dependency graph. This
data is serialized and provided as an `"auditReportVersion": 2` object.

Version 2 of this module expects to recieve an instance (or serialized JSON
version of) the `AuditReport` class from Arborist, which is returned by
`arborist.audit()` and stored on the instance as `arborist.auditReport`.

Eventually, a new endpoint _may_ be added to move the `@npmcli/arborist` work
to the server-side, in which case version 2 style audit reports _may_ be
provided directly.

## options

| option | values | default | description |
| :--- | :--- | :--- |:--- |
| reporter     | `install`, `detail`, `json`, `quiet` | `install` | specify which output format you want to use |
| withColor     | `true`, `false`   | `true`   | indicates if some report elements should use colors |
| withUnicode   | `true`, `false`                  | `true` | indicates if unicode characters should be used|
| option | values | default | description |
| :--- | :--- | :--- |:--- |
| reporter | `install`, `detail`, `json`, `quiet` | `install` | specify which output format you want to use |
| color   | `true`, `false`   | `true`   | indicates if some report elements should use colors |
| unicode  | `true`, `false`                  | `true` | indicates if unicode characters should be used|
| indent   | Number or String                | `2` | indentation for `'json'` report|
25 changes: 0 additions & 25 deletions index.js

This file was deleted.

24 changes: 24 additions & 0 deletions lib/colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const chalk = require('chalk')
module.exports = color => {
const identity = x => x
const green = color ? s => chalk.green.bold(s) : identity
const red = color ? s => chalk.red.bold(s) : identity
const magenta = color ? s => chalk.magenta.bold(s) : identity
const yellow = color ? s => chalk.yellow.bold(s) : identity
const white = color ? s => chalk.bold(s) : identity
const severity = (sev, s) => sev.toLowerCase() === 'moderate' ? yellow(s || sev)
: sev.toLowerCase() === 'high' ? red(s || sev)
: sev.toLowerCase() === 'critical' ? magenta(s || sev)
: white(s || sev)
const dim = color ? s => chalk.dim(s) : identity

return {
dim,
green,
red,
magenta,
yellow,
white,
severity
}
}
22 changes: 22 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const reporters = {
install: require('./reporters/install'),
detail: require('./reporters/detail'),
json: require('./reporters/json'),
quiet: require('./reporters/quiet')
}

module.exports = Object.assign((data, options = {}) => {
const {
reporter = 'install',
color = true,
unicode = true,
indent = 2
} = options

if (typeof data.toJSON === 'function')
data = data.toJSON()

return reporters[reporter](data, { color, unicode, indent })
}, { reporters })
86 changes: 86 additions & 0 deletions lib/reporters/detail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use strict'

const colors = require('../colors.js')
const install = require('./install.js')

module.exports = (data, { color }) => {
const {summary, exitCode} = install(data, { color })
return {
exitCode,
report: exitCode === 0 ? summary : fullReport(data, {color, summary})
}
}

const fullReport = (data, { color, summary }) => {
const c = colors(color)
const output = [c.white('# npm audit report'), '']

const printed = new Set()
for (const [name, vuln] of Object.entries(data.vulnerabilities)) {
if (printed.has(vuln))
continue

printed.add(vuln)
output.push(printVuln(vuln, c, data.vulnerabilities, printed))
}

output.push(summary)

return output.join('\n')
}

const printVuln = (vuln, c, vulnerabilities, printed, indent = '') => {
const output = []

output.push(c.white(vuln.name) + ' ' + vuln.range)

if (indent === '' && (vuln.severity !== 'low' || vuln.severity === 'info')) {
output.push(`Severity: ${c.severity(vuln.severity)}`)
}

for (const via of vuln.via) {
if (typeof via === 'string') {
output.push(`Depends on vulnerable versions of ${c.white(via)}`)
} else if (indent === '') {
output.push(`${c.white(via.title)} - ${via.url}`)
}
}

if (indent === '') {
const { fixAvailable: fa } = vuln
if (fa === false) {
output.push(c.red('No fix available'))
} else if (fa === true) {
output.push(c.green('fix available') + ' via `npm audit fix`')
} else {
/* istanbul ignore else - should be impossible, just being cautious */
if (typeof fa === 'object' && indent === '') {
output.push(
`${c.yellow('fix available')} via \`npm audit fix --force\``,
`Will install ${fa.name}@${fa.version}` +
`, which is ${fa.isSemVerMajor ? 'a breaking change' :
'outside the stated dependency range' }`
)
}
}
}

for (const path of vuln.nodes) {
output.push(c.dim(path))
}

for (const effect of vuln.effects) {
const vuln = vulnerabilities[effect]
// still print it again if it has its own advisory as well
if (vuln.via.filter(v => typeof v !== 'string').length === 0)
printed.add(vuln)
const e = printVuln(vuln, c, vulnerabilities, printed, ' ')
output.push(...e.split('\n'))
}

if (indent === '') {
output.push('')
}

return output.map(l => `${indent}${l}`).join('\n')
}
Loading

0 comments on commit b3d5fcc

Please sign in to comment.