Skip to content

Commit

Permalink
Merge pull request #47 from quinnturner/feat/registry
Browse files Browse the repository at this point in the history
feat(registry): Add support for specifying registry
  • Loading branch information
quinnturner authored Mar 6, 2019
2 parents ee64986 + e30fa53 commit 6aade22
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 32 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ before_install:
| -a | --advisories | Vulnerable advisory ids to whitelist from preventing integration (default `none`) |
| -w | --whitelist | Vulnerable modules to whitelist from preventing integration (default `none`) |
| -d | --directory | The directory containing the package.json to audit. (default `./`) |
| | --registry | The registry to resolve packages by name and version (default to unspecified) |
| | --config | Path to JSON config file |

### (_Optional_) Config file specification
Expand All @@ -96,7 +97,8 @@ A config file can manage auditing preferences `audit-ci`. The config file's keys
"summary": <boolean>, // [Optional] defaults `true`
"package-manager": <string>, // [Optional] defaults `"auto"`
"advisories": <number[]>, // [Optional] defaults `[]`
"whitelist": <string[]> // [Optional] defaults `[]`
"whitelist": <string[]>, // [Optional] defaults `[]`,
"registry": <string> // [Optional] defaults `undefined`
}
```
Expand Down Expand Up @@ -140,7 +142,8 @@ audit-ci
"low": true,
"package-manager": "auto",
"advisories": [100, 101],
"whitelist": ["example1", "example2"]
"whitelist": ["example1", "example2"],
"registry": "https://registry.npmjs.org"
}
```

Expand Down
5 changes: 5 additions & 0 deletions lib/audit-ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const { argv } = yargs
describe: 'The directory containing the package.json to audit',
type: 'string',
},
registry: {
default: undefined,
describe: 'The registry to resolve packages by name and version',
type: 'string',
},
})
.help('help');

Expand Down
14 changes: 10 additions & 4 deletions lib/npm-auditer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
const { runProgram, reportAudit } = require('./common');
const Model = require('./Model');

function runNpmAudit(directory) {
function runNpmAudit(config) {
const { directory, registry } = config;

const stdoutBuffer = [];
function outListener(line) {
stdoutBuffer.push(line);
Expand All @@ -18,6 +20,9 @@ function runNpmAudit(directory) {
}

const args = ['audit', '--json'];
if (registry) {
args.push('--registry', registry);
}
const options = { cwd: directory };
return Promise.resolve()
.then(() => runProgram('npm', args, options, outListener, errListener))
Expand All @@ -32,7 +37,7 @@ function runNpmAudit(directory) {
try {
return JSON.parse(stdout);
} catch (e) {
console.log(stdout);
console.error(stdout);
throw e;
}
});
Expand All @@ -53,17 +58,18 @@ function printReport(parsedOutput, report) {
/**
* Audit your NPM project!
*
* @param {{directory: string, report: { full?: boolean, summary?: boolean }, whitelist: string[], advisories: string[], levels: { low: boolean, moderate: boolean, high: boolean, critical: boolean }}} config
* @param {{directory: string, report: { full?: boolean, summary?: boolean }, whitelist: string[], advisories: string[], registry: string, levels: { low: boolean, moderate: boolean, high: boolean, critical: boolean }}} config
* `directory`: the directory containing the package.json to audit.
* `report`: report level: `full` for full report, `summary` for summary
* `whitelist`: a list of packages that should not break the build if their vulnerability is found.
* `advisories`: a list of advisory ids that should not break the build if found.
* `registry`: the registry to resolve packages by name and version.
* `levels`: the vulnerability levels to fail on, if `moderate` is set `true`, `high` and `critical` should be as well.
* @returns {Promise<any>} Returns the audit report summary on resolve, `Error` on rejection.
*/
function audit(config, reporter = reportAudit) {
return Promise.resolve()
.then(() => runNpmAudit(config.directory))
.then(() => runNpmAudit(config))
.then(parsedOutput => printReport(parsedOutput, config.report))
.then(parsedOutput =>
reporter(new Model(config).load(parsedOutput), config, parsedOutput)
Expand Down
26 changes: 24 additions & 2 deletions lib/yarn-auditer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const { reportAudit, runProgram } = require('./common');
const Model = require('./Model');

const MINIMUM_YARN_VERSION = '1.12.3';
/**
* Change this to the appropriate version when
* yarn audit --registry is supported:
* @see https://github.com/yarnpkg/yarn/issues/7012
*/
const MINIMUM_YARN_AUDIT_REGISTRY_VERSION = '99.99.99';

function getYarnVersion() {
const version = childProcess
Expand All @@ -22,20 +28,25 @@ function yarnSupportsAudit(yarnVersion) {
return semver.gte(yarnVersion, MINIMUM_YARN_VERSION);
}

function yarnAuditSupportsRegistry(yarnVersion) {
return semver.gte(yarnVersion, MINIMUM_YARN_AUDIT_REGISTRY_VERSION);
}

/**
* Audit your Yarn project!
*
* @param {{directory: string, report: { full?: boolean, summary?: boolean }, whitelist: string[], advisories: string[], levels: { low: boolean, moderate: boolean, high: boolean, critical: boolean }}} config
* @param {{directory: string, report: { full?: boolean, summary?: boolean }, whitelist: string[], advisories: string[], registry: string, levels: { low: boolean, moderate: boolean, high: boolean, critical: boolean }}} config
* `directory`: the directory containing the package.json to audit.
* `report`: report level: `full` for full report, `summary` for summary
* `whitelist`: a list of packages that should not break the build if their vulnerability is found.
* `advisories`: a list of advisory ids that should not break the build if found.
* `registry`: the registry to resolve packages by name and version.
* `levels`: the vulnerability levels to fail on, if `moderate` is set `true`, `high` and `critical` should be as well.
* @returns {Promise<any>} Returns the audit report summary on resolve, `Error` on rejection.
*/
function audit(config, reporter = reportAudit) {
return Promise.resolve().then(() => {
const { report, whitelist } = config;
const { registry, report, whitelist } = config;
let missingLockFile = false;
const model = new Model(config);

Expand Down Expand Up @@ -90,6 +101,17 @@ function audit(config, reporter = reportAudit) {
}
const options = { cwd: config.directory };
const args = ['audit', '--json'];
if (registry) {
const auditRegistrySupported = yarnAuditSupportsRegistry(yarnVersion);
if (auditRegistrySupported) {
args.push('--registry', registry);
} else {
console.warn(
'\x1b[33m%s\x1b[0m',
'Yarn audit does not support the registry flag yet.'
);
}
}
return runProgram('yarn', args, options, outListener, errListener).then(
() => {
if (missingLockFile) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
],
"scripts": {
"lint": "eslint -c ./.eslintrc.json lib/*",
"test": "mocha --exit --timeout 60000 --recursive --reporter spec test/*.js"
"test": "mocha --exit --timeout 40000 --recursive --reporter spec test/*.js"
},
"dependencies": {
"byline": "^5.0.0",
Expand Down
2 changes: 1 addition & 1 deletion test/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ describe('Model', () => {
});
});

it('ignores whitelisted advisotry IDs', () => {
it('ignores whitelisted advisory IDs', () => {
const model = new Model({
levels: { critical: true, low: true, high: true, moderate: true },
whitelist: [],
Expand Down
54 changes: 43 additions & 11 deletions test/npm-auditer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,36 @@ const path = require('path');
const { audit } = require('../lib/npm-auditer');

function config(additions) {
return Object.assign({}, { whitelist: [], advisories: [] }, additions);
const defaultConfig = {
levels: {
low: false,
moderate: false,
high: false,
critical: false,
},
report: {},
advisories: [],
whitelist: [],
directory: './',
registry: undefined,
};
return Object.assign({}, defaultConfig, additions);
}

function testDir(s) {
return path.resolve(__dirname, s);
}

describe('npm-auditer', () => {
// To modify what slow times are, need to use
// function() {} instead of () => {}
// eslint-disable-next-line func-names
describe('npm-auditer', function() {
this.slow(6000);
it('reports critical severity', () => {
return audit(
config({
directory: testDir('npm-critical'),
levels: { critical: true },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -38,7 +54,6 @@ describe('npm-auditer', () => {
config({
directory: testDir('npm-critical'),
levels: { critical: false },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -55,7 +70,6 @@ describe('npm-auditer', () => {
config({
directory: testDir('npm-high'),
levels: { high: true },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -72,7 +86,6 @@ describe('npm-auditer', () => {
config({
directory: testDir('npm-moderate'),
levels: { moderate: true },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -89,7 +102,6 @@ describe('npm-auditer', () => {
config({
directory: testDir('npm-moderate'),
levels: { moderate: false },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -107,7 +119,6 @@ describe('npm-auditer', () => {
directory: testDir('npm-moderate'),
levels: { moderate: true },
advisories: [658],
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -125,7 +136,6 @@ describe('npm-auditer', () => {
directory: testDir('npm-moderate'),
levels: { moderate: true },
advisories: [659],
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -142,7 +152,6 @@ describe('npm-auditer', () => {
config({
directory: testDir('npm-low'),
levels: { low: true },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -159,7 +168,6 @@ describe('npm-auditer', () => {
config({
directory: testDir('npm-none'),
levels: { low: true },
report: {},
}),
summary => summary
).then(summary => {
Expand All @@ -171,4 +179,28 @@ describe('npm-auditer', () => {
});
});
});
it('fails with error code ENOTFOUND on a non-existent site', done => {
audit(
config({
directory: testDir('npm-low'),
levels: { low: true },
registry: 'https://registry.nonexistentdomain0000000000.com',
})
).catch(err => {
expect(err.message).to.include('code ENOTFOUND');
done();
});
});
it('fails errors with code ENOAUDIT on a valid site with no audit', done => {
audit(
config({
directory: testDir('npm-low'),
levels: { low: true },
registry: 'https://example.com',
})
).catch(err => {
expect(err.message).to.include('code ENOAUDIT');
done();
});
});
});
Loading

0 comments on commit 6aade22

Please sign in to comment.