Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(audit): select which dependency groups to audit #6724

Merged
merged 3 commits into from Mar 16, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)

## 1.15.2

The 1.15.1 doesn't exist due to a release hiccup.
Expand Down
98 changes: 98 additions & 0 deletions __tests__/commands/audit.js
Expand Up @@ -106,6 +106,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
29 changes: 20 additions & 9 deletions src/cli/commands/audit.js
Expand Up @@ -11,11 +11,15 @@ import {buildTree as hoistedTreeBuilder} from '../../hoisted-tree-builder';
import {getTransitiveDevDependencies} from '../../util/get-transitive-dev-dependencies';
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 @@ -117,14 +121,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 Down Expand Up @@ -157,13 +167,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, transitiveDevDeps: Set<string>) {
Expand All @@ -189,6 +201,10 @@ export default class Audit {
}

_mapHoistedTreesToAuditTree(manifest: Object, hoistedTrees: HoistedTrees, transitiveDevDeps: Set<string>): 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 @@ -197,12 +213,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: {},
dev: false,
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/install.js
Expand Up @@ -578,7 +578,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