Skip to content

Commit

Permalink
feat(audit): select which dependency groups to audit
Browse files Browse the repository at this point in the history
  • Loading branch information
tommilligan committed Nov 28, 2018
1 parent d817134 commit 0a2199d
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,10 @@ Please add one entry in this file for each change in Yarn's behavior. Use the sa

## Master

- Implements `yarn audit --groups group_name [group_name ...]`.

[#6724](https://github.com/yarnpkg/yarn/pull/6724) - [**Tom Milligan**](https://github.com/tommilligan)

- Implements `yarn policies set-version [range]`. Check [the documentation]() for usage & tips.

[#6673](https://github.com/yarnpkg/yarn/pull/6673) - [**Maël Nison**](https://twitter.com/arcanis)
Expand Down
98 changes: 98 additions & 0 deletions __tests__/commands/audit.js
Expand Up @@ -98,6 +98,104 @@ test.concurrent('sends correct dependency map to audit api for single dependency
});
});

test('audit groups dependencies does not affect requires', () => {
const expectedApiPost = {
name: 'yarn-test',
install: [],
remove: [],
metadata: {},
requires: {
minimatch: '^3.0.0',
},
dependencies: {
minimatch: {
version: '3.0.0',
integrity: 'sha1-UjYVelHk8ATBd/s8Un/33Xjw74M=',
requires: {
'brace-expansion': '^1.0.0',
},
dependencies: {},
},
'brace-expansion': {
version: '1.1.11',
integrity: 'sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==',
requires: {
'balanced-match': '^1.0.0',
'concat-map': '0.0.1',
},
dependencies: {},
},
'balanced-match': {
version: '1.0.0',
integrity: 'sha1-ibTRmasr7kneFk6gK4nORi1xt2c=',
requires: {},
dependencies: {},
},
'concat-map': {
version: '0.0.1',
integrity: 'sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=',
requires: {},
dependencies: {},
},
},
version: '0.0.0',
};

return runAudit([], {groups: ['dependencies']}, 'single-vulnerable-dep-installed', async config => {
const calledWithPipe = config.requestManager.request.mock.calls[0][0].body;
const calledWith = JSON.parse(await gunzip(calledWithPipe));
expect(calledWith).toEqual(expectedApiPost);
});
});

test('audit groups only devDependencies omits dependencies from requires', () => {
const expectedApiPost = {
name: 'yarn-test',
install: [],
remove: [],
metadata: {},
requires: {},
dependencies: {
minimatch: {
version: '3.0.0',
integrity: 'sha1-UjYVelHk8ATBd/s8Un/33Xjw74M=',
requires: {
'brace-expansion': '^1.0.0',
},
dependencies: {},
},
'brace-expansion': {
version: '1.1.11',
integrity: 'sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==',
requires: {
'balanced-match': '^1.0.0',
'concat-map': '0.0.1',
},
dependencies: {},
},
'balanced-match': {
version: '1.0.0',
integrity: 'sha1-ibTRmasr7kneFk6gK4nORi1xt2c=',
requires: {},
dependencies: {},
},
'concat-map': {
version: '0.0.1',
integrity: 'sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=',
requires: {},
dependencies: {},
},
},
version: '0.0.0',
};

return runAudit([], {groups: ['devDependencies']}, 'single-vulnerable-dep-installed', async config => {
const calledWithPipe = config.requestManager.request.mock.calls[0][0].body;
const calledWith = JSON.parse(await gunzip(calledWithPipe));
expect(calledWith).toEqual(expectedApiPost);
});
});

test('calls reporter auditAdvisory with correct data', () => {
return runAudit([], {}, 'single-vulnerable-dep-installed', (config, reporter) => {
const apiResponse = getAuditResponse(config);
Expand Down
28 changes: 19 additions & 9 deletions src/cli/commands/audit.js
Expand Up @@ -10,11 +10,15 @@ import {promisify} from '../../util/promise.js';
import {buildTree as hoistedTreeBuilder} from '../../hoisted-tree-builder';
import {Install} from './install.js';
import Lockfile from '../../lockfile';
import {YARN_REGISTRY} from '../../constants';
import {OWNED_DEPENDENCY_TYPES, YARN_REGISTRY} from '../../constants';

const zlib = require('zlib');
const gzip = promisify(zlib.gzip);

export type AuditOptions = {
groups: Array<string>,
};

export type AuditNode = {
version: ?string,
integrity: ?string,
Expand Down Expand Up @@ -115,14 +119,20 @@ export type AuditActionRecommendation = {
export function setFlags(commander: Object) {
commander.description('Checks for known security issues with the installed packages.');
commander.option('--summary', 'Only print the summary.');
commander.option(
'--groups <group_name> [<group_name> ...]',
`Only audit dependencies from listed groups. Default: ${OWNED_DEPENDENCY_TYPES.join(', ')}`,
groups => groups.split(' '),
OWNED_DEPENDENCY_TYPES,
);
}

export function hasWrapper(commander: Object, args: Array<string>): boolean {
return true;
}

export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<number> {
const audit = new Audit(config, reporter);
const audit = new Audit(config, reporter, {groups: flags.groups || OWNED_DEPENDENCY_TYPES});
const lockfile = await Lockfile.fromDirectory(config.lockfileFolder, reporter);
const install = new Install({}, config, reporter, lockfile);
const {manifest, requests, patterns, workspaceLayout} = await install.fetchRequestFromCwd();
Expand All @@ -148,13 +158,15 @@ export async function run(config: Config, reporter: Reporter, flags: Object, arg
}

export default class Audit {
constructor(config: Config, reporter: Reporter) {
constructor(config: Config, reporter: Reporter, options: AuditOptions) {
this.config = config;
this.reporter = reporter;
this.options = options;
}

config: Config;
reporter: Reporter;
options: AuditOptions;
auditData: AuditReport;

_mapHoistedNodes(auditNode: AuditNode, hoistedNodes: HoistedTrees) {
Expand All @@ -179,6 +191,9 @@ export default class Audit {
}

_mapHoistedTreesToAuditTree(manifest: Object, hoistedTrees: HoistedTrees): AuditTree {
const requiresGroups = this.options.groups.map(function(group: string): Object {
return manifest[group] || {};
});
const auditTree: AuditTree = {
name: manifest.name || undefined,
version: manifest.version || undefined,
Expand All @@ -187,12 +202,7 @@ export default class Audit {
metadata: {
//TODO: What do we send here? npm sends npm version, node version, etc.
},
requires: Object.assign(
{},
manifest.dependencies || {},
manifest.devDependencies || {},
manifest.optionalDependencies || {},
),
requires: Object.assign({}, ...requiresGroups),
integrity: undefined,
dependencies: {},
};
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/install.js
Expand Up @@ -579,7 +579,7 @@ export class Install {
});
}

const audit = new Audit(this.config, this.reporter);
const audit = new Audit(this.config, this.reporter, {groups: constants.OWNED_DEPENDENCY_TYPES});
let auditFoundProblems = false;

steps.push((curr: number, total: number) =>
Expand Down

0 comments on commit 0a2199d

Please sign in to comment.