Skip to content

Commit

Permalink
core(vulnerable-libs): add fix for recovering from bad versions (#3932)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhulce authored and paulirish committed Nov 29, 2017
1 parent 6910f5d commit e842834
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 2 deletions.
33 changes: 31 additions & 2 deletions lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
'use strict';

const Audit = require('../audit');
const Sentry = require('../../lib/sentry');
const semver = require('semver');
const snykDatabase = require('../../../third-party/snyk/snapshot.json');

const SEMVER_REGEX = /^(\d+\.\d+\.\d+)[^-0-9]+/;

class NoVulnerableLibrariesAudit extends Audit {
/**
* @return {!AuditMeta}
Expand Down Expand Up @@ -51,6 +54,23 @@ class NoVulnerableLibrariesAudit extends Audit {
};
}

/**
* Attempts to normalize the version.
* @param {?string} version
* @return {?string}
*/
static normalizeVersion(version) {
if (!version) return version;
if (semver.valid(version)) return version;

// converts 1.5 -> 1.5.0
if (/^\d+\.\d+$/.test(version)) return `${version}.0`;
// converts 1.0.0a-bunch-of-crap -> 1.0.0
if (SEMVER_REGEX.test(version)) return version.match(SEMVER_REGEX)[1];
// leave everything else untouched
return version;
}

/**
* @param {{name: string, version: string, npmPkgName: string|undefined}} lib
* @param {{npm: !Object<string, !Array<{id: string, severity: string, semver: {vulnerable: !Array<string>}}>>}} snykDB
Expand All @@ -62,8 +82,16 @@ class NoVulnerableLibrariesAudit extends Audit {
return vulns;
}

lib.pkgLink = 'https://snyk.io/vuln/npm:' + lib.npmPkgName
+ '#lh@' + lib.version;
try {
semver.satisfies(lib.version, '*');
} catch (err) {
err.pkgName = lib.npmPkgName;
// Report the failure and skip this library if the version was ill-specified
Sentry.captureException(err, {level: 'warning'});
return vulns;
}

lib.pkgLink = `https://snyk.io/vuln/npm:${lib.npmPkgName}#lh@${lib.version}`;
const snykInfo = snykDB.npm[lib.npmPkgName];
snykInfo.forEach(vuln => {
if (semver.satisfies(lib.version, vuln.semver.vulnerable[0])) {
Expand Down Expand Up @@ -103,6 +131,7 @@ class NoVulnerableLibrariesAudit extends Audit {

let totalVulns = 0;
const finalVulns = libraries.map(lib => {
lib.version = this.normalizeVersion(lib.version);
lib.vulns = this.getVulns(lib, this.snykDB);
if (lib.vulns.length > 0) {
lib.vulnCount = lib.vulns.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ const assert = require('assert');

/* eslint-env mocha */
describe('Avoids front-end JavaScript libraries with known vulnerabilities', () => {
describe('#normalizeVersion', () => {
it('should leave valid and unsavable versions untouched', () => {
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion(null), null);
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion('52.1.13'), '52.1.13');
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion('52.1.13-rc.1'), '52.1.13-rc.1');
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion('c0ab71056b936'), 'c0ab71056b936');
});

it('should fix bad version numbers', () => {
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion('11.51'), '11.51.0');
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion('12.27.14 -junk'), '12.27.14');
assert.equal(NoVulnerableLibrariesAudit.normalizeVersion('12.27.14_other-junk'), '12.27.14');
});
});

it('fails when JS libraries with known vulnerabilities are detected', () => {
const auditResult = NoVulnerableLibrariesAudit.audit({
JSLibraries: [
Expand All @@ -27,6 +42,20 @@ describe('Avoids front-end JavaScript libraries with known vulnerabilities', ()
assert.equal(auditResult.details.items[0][0].url, 'https://snyk.io/vuln/npm:angular#lh@1.1.4');
});

it('handles ill-specified versions', () => {
const auditResult = NoVulnerableLibrariesAudit.audit({
JSLibraries: [
{name: 'angular', version: 'c0ab71056b936', npmPkgName: 'angular'},
{name: 'react', version: '1.5.0 -something,weird', npmPkgName: 'react'},
{name: 'jquery', version: '1.8', npmPkgName: 'jquery'},
],
});

assert.equal(auditResult.rawValue, false);
assert.equal(auditResult.details.items.length, 1);
assert.equal(auditResult.details.items[0][0].text, 'jquery@1.8.0');
});

it('passes when no JS libraries with known vulnerabilities are detected', () => {
const auditResult = NoVulnerableLibrariesAudit.audit({
JSLibraries: [
Expand Down

0 comments on commit e842834

Please sign in to comment.