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(stack-packs): added draft for stack packs #7243

Merged
merged 29 commits into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
663c532
feat(stack-packs): added draft for stack packs
wardpeet Feb 13, 2019
d7802b2
update stack pack
wardpeet Mar 5, 2019
81a7589
move to detector instead of type
wardpeet Mar 5, 2019
f74b631
move stackpacks
wardpeet Mar 25, 2019
9d758ba
update stack packs package structure
wardpeet Mar 25, 2019
4ff4ce0
upgrade library-detector to detect wordpress
wardpeet Mar 25, 2019
6f8da71
add typings to stackpacks
wardpeet Mar 25, 2019
bdfc211
add stack mapper
wardpeet Mar 25, 2019
5033373
fix stack packs check
wardpeet Mar 26, 2019
baf0685
fixed typings on stack-packs
wardpeet Mar 26, 2019
5b6f984
Move JSLibraries into Stack Packs
wardpeet Mar 26, 2019
1f3ec52
update lhr & auditref
wardpeet Mar 26, 2019
6ec27ac
rename npmPkgName => npm
wardpeet Apr 8, 2019
6df0151
rename npmOrIconName => detectedId
wardpeet Apr 8, 2019
bac38ae
fix stack packs typings
wardpeet Apr 8, 2019
284c360
review changes to getStackpack
wardpeet Apr 8, 2019
096dafc
update stack-packs renderer
wardpeet Apr 8, 2019
c0f5791
move inline styling to report-styles.css
wardpeet Apr 9, 2019
897ea52
Update wordpress.js
connorjclark Apr 9, 2019
364919a
Added stack pack proto def. (#8116)
exterkamp Apr 9, 2019
a748752
Move stack gatherer to base
wardpeet Apr 9, 2019
d568b64
Fix tests
wardpeet Apr 9, 2019
1046ecd
Merge branch 'master' into feat/stack-packs
wardpeet Apr 9, 2019
156a4ed
fix proto test
wardpeet Apr 9, 2019
fa8e755
fix js extensions
wardpeet Apr 9, 2019
ab84106
fix faulty merge tests
wardpeet Apr 9, 2019
456f598
adds links to a bunch of wordpress stack strings (#8079)
housseindjirdeh Apr 10, 2019
57ae9ca
Add wordpress.js changes from other branch.
exterkamp Apr 10, 2019
d7ff561
update snapshot
brendankenny Apr 11, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions lighthouse-core/audits/dobetterweb/js-libraries.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class JsLibrariesAudit extends Audit {
id: 'js-libraries',
title: 'Detected JavaScript libraries',
description: 'All front-end JavaScript libraries detected on the page.',
requiredArtifacts: ['JSLibraries'],
requiredArtifacts: ['Stacks'],
};
}

Expand All @@ -30,11 +30,13 @@ class JsLibrariesAudit extends Audit {
* @return {LH.Audit.Product}
*/
static audit(artifacts) {
const libDetails = artifacts.JSLibraries.map(lib => ({
name: lib.name,
version: lib.version || undefined, // null if not detected
npm: lib.npmPkgName || undefined, // ~70% of libs come with this field
}));
const libDetails = (artifacts.Stacks || [])
.filter(stack => stack.detector === 'js')
.map(stack => ({
name: stack.name,
version: stack.version || undefined, // null if not detected
npm: stack.npm || undefined, // ~70% of libs come with this field
}));

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
Expand Down
16 changes: 8 additions & 8 deletions lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class NoVulnerableLibrariesAudit extends Audit {
description: 'Some third-party scripts may contain known security vulnerabilities ' +
'that are easily identified and exploited by attackers. ' +
'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/vulnerabilities).',
requiredArtifacts: ['JSLibraries'],
requiredArtifacts: ['Stacks'],
};
}

Expand Down Expand Up @@ -78,27 +78,27 @@ class NoVulnerableLibrariesAudit extends Audit {

/**
* @param {string} normalizedVersion
* @param {{name: string, version: string, npmPkgName: string|undefined}} lib
* @param {{name: string, version: string, npm?: string}} lib
* @param {SnykDB} snykDB
* @return {Array<Vulnerability>}
*/
static getVulnerabilities(normalizedVersion, lib, snykDB) {
if (!lib.npmPkgName || !snykDB.npm[lib.npmPkgName]) {
if (!lib.npm || !snykDB.npm[lib.npm]) {
return [];
}

// Verify the version is well-formed first
try {
semver.satisfies(normalizedVersion, '*');
} catch (err) {
err.pkgName = lib.npmPkgName;
err.pkgName = lib.npm;
// Report the failure and skip this library if the version was ill-specified
Sentry.captureException(err, {level: 'warning'});
return [];
}

// Match the vulnerability candidates from snyk against the version we see in the page
const vulnCandidatesForLib = snykDB.npm[lib.npmPkgName];
const vulnCandidatesForLib = snykDB.npm[lib.npm];
const matchingVulns = vulnCandidatesForLib.filter(vulnCandidate => {
// Each snyk vulnerability comes with an array of semver ranges
// The page is vulnerable if any of the ranges match.
Expand Down Expand Up @@ -135,7 +135,7 @@ class NoVulnerableLibrariesAudit extends Audit {
* @return {LH.Audit.Product}
*/
static audit(artifacts) {
const foundLibraries = artifacts.JSLibraries;
const foundLibraries = (artifacts.Stacks || []).filter(stack => stack.detector === 'js');
const snykDB = NoVulnerableLibrariesAudit.snykDB;

if (!foundLibraries.length) {
Expand Down Expand Up @@ -163,15 +163,15 @@ class NoVulnerableLibrariesAudit extends Audit {
vulnCount,
detectedLib: {
text: lib.name + '@' + version,
url: `https://snyk.io/vuln/npm:${lib.npmPkgName}?lh=${version}&utm_source=lighthouse&utm_medium=ref&utm_campaign=audit`,
url: `https://snyk.io/vuln/npm:${lib.npm}?lh=${version}&utm_source=lighthouse&utm_medium=ref&utm_campaign=audit`,
type: 'link',
},
});
}

return {
name: lib.name,
npmPkgName: lib.npmPkgName,
npmPkgName: lib.npm,
version,
vulns,
highestSeverity,
Expand Down
1 change: 0 additions & 1 deletion lighthouse-core/config/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ const defaultConfig = {
'dobetterweb/appcache',
'dobetterweb/doctype',
'dobetterweb/domstats',
'dobetterweb/js-libraries',
'dobetterweb/optimized-images',
'dobetterweb/password-inputs-with-prevented-paste',
'dobetterweb/response-compression',
Expand Down
3 changes: 3 additions & 0 deletions lighthouse-core/gather/gather-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

const log = require('lighthouse-logger');
const manifestParser = require('../lib/manifest-parser.js');
const stacksGatherer = require('../lib/gatherer-stacks.js');
const LHError = require('../lib/lh-error.js');
const URL = require('../lib/url-shim.js');
const NetworkRecorder = require('../lib/network-recorder.js');
Expand Down Expand Up @@ -411,6 +412,7 @@ class GatherRunner {
NetworkUserAgent: '', // updated later
BenchmarkIndex: 0, // updated later
WebAppManifest: null, // updated later
Stacks: null, // updated later
traces: {},
devtoolsLogs: {},
settings: options.settings,
Expand Down Expand Up @@ -480,6 +482,7 @@ class GatherRunner {
await GatherRunner.pass(passContext, gathererResults);
if (isFirstPass) {
baseArtifacts.WebAppManifest = await GatherRunner.getWebAppManifest(passContext);
baseArtifacts.Stacks = await stacksGatherer(passContext);
}
const passData = await GatherRunner.afterPass(passContext, gathererResults);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @license Copyright 2018 Google Inc. All Rights Reserved.
* @license Copyright 2019 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
Expand All @@ -13,51 +13,72 @@

'use strict';

const Gatherer = require('../gatherer');
const fs = require('fs');
const libDetectorSource = fs.readFileSync(
require.resolve('js-library-detector/library/libraries.js'), 'utf8');
require.resolve('js-library-detector/library/libraries.js'),
'utf8'
);

/**
* @typedef JSLibrary
* @property {string} name
* @property {string} version
* @property {string} npm
* @property {string} iconName
*/

/**
* Obtains a list of detected JS libraries and their versions.
*/
/* istanbul ignore next */
function detectLibraries() {
/** @type {LH.Artifacts['JSLibraries']} */
/** @type {JSLibrary[]} */
const libraries = [];

// d41d8cd98f00b204e9800998ecf8427e_ is a consistent prefix used by the detect libraries
// see https://github.com/HTTPArchive/httparchive/issues/77#issuecomment-291320900
// @ts-ignore - injected libDetectorSource var
Object.entries(d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests).forEach(async ([name, lib]) => { // eslint-disable-line max-len
try {
const result = await lib.test(window);
if (result) {
libraries.push({
name: name,
version: result.version,
npmPkgName: lib.npm,
});
}
} catch (e) {}
});
Object.entries(d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests).forEach(
async ([name, lib]) => {
// eslint-disable-line max-len
try {
const result = await lib.test(window);
if (result) {
libraries.push({
name: name,
version: result.version,
npm: lib.npm,
iconName: lib.icon,
});
}
} catch (e) {}
}
);

return libraries;
}

class JSLibraries extends Gatherer {
/**
* @param {LH.Gatherer.PassContext} passContext
* @return {Promise<LH.Artifacts['JSLibraries']>}
*/
afterPass(passContext) {
const expression = `(function () {
${libDetectorSource};
return (${detectLibraries.toString()}());
})()`;

return passContext.driver.evaluateAsync(expression);
}
/**
* @param {LH.Gatherer.PassContext} passContext
* @return {Promise<LH.Artifacts['Stacks']>}
*/
async function getStacks(passContext) {
const expression = `(function () {
${libDetectorSource};
return (${detectLibraries.toString()}());
})()`;

const jsLibraries = /** @type {JSLibrary[]} */ (await passContext.driver.evaluateAsync(
expression
));

return jsLibraries.map(lib => ({
detector: /** @type {'js'} */ ('js'),
id: lib.npm || lib.iconName,
name: lib.name,
version: lib.version,
npm: lib.npm,
}));
}

module.exports = JSLibraries;
module.exports = getStacks;
52 changes: 52 additions & 0 deletions lighthouse-core/lib/stack-packs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* @license Copyright 2019 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';

const stackPacks = require('@lighthouse/stack-packs');

wardpeet marked this conversation as resolved.
Show resolved Hide resolved
const stackPacksToInclude = [{
wardpeet marked this conversation as resolved.
Show resolved Hide resolved
packId: 'wordpress',
requiredStacks: ['js:wordpress'],
}];
wardpeet marked this conversation as resolved.
Show resolved Hide resolved

/**
* @param {LH.Artifacts} artifacts
* @return {Array<LH.Result.StackPack>}
*/
function getStackPacks(artifacts) {
/** @type {Array<LH.Result.StackPack>} */
const packs = [];

if (artifacts.Stacks) {
for (const pageStack of artifacts.Stacks) {
const stackPackToIncl = stackPacksToInclude.find(stackPackToIncl =>
stackPackToIncl.requiredStacks.includes(`${pageStack.detector}:${pageStack.id}`));
if (!stackPackToIncl) {
continue;
}

// Grab the full pack definition
const matchedPack = stackPacks.find(pack => pack.id === stackPackToIncl.packId);
if (!matchedPack) {
// we couldn't find a pack that's in our inclusion list, this is weird.
continue;
}

packs.push({
id: matchedPack.id,
title: matchedPack.title,
iconDataURL: matchedPack.iconDataURL,
descriptions: matchedPack.descriptions,
});
}
}

return packs;
}

module.exports = {
getStackPacks,
};
18 changes: 18 additions & 0 deletions lighthouse-core/report/html/renderer/category-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ class CategoryRenderer {
this.dom.find('.lh-audit__description', auditEl)
.appendChild(this.dom.convertMarkdownLinkSnippets(audit.result.description));

if (audit.result.stackPacks) {
audit.result.stackPacks.forEach(pack => {
const packElm = this.dom.createElement('div');
packElm.classList.add('lh-audit__stackpack');

const packElmImg = this.dom.createElement('img');
packElmImg.classList.add('lh-audit__stackpack__img');
packElmImg.src = pack.iconDataURL;
packElmImg.alt = pack.title;
packElm.appendChild(packElmImg);

packElm.appendChild(this.dom.convertMarkdownLinkSnippets(pack.description));

this.dom.find('.lh-audit__stackpacks', auditEl)
.appendChild(packElm);
});
}

const header = /** @type {HTMLDetailsElement} */ (this.dom.find('details', auditEl));
if (audit.result.details) {
const elem = this.detailsRenderer.render(audit.result.details);
Expand Down
14 changes: 14 additions & 0 deletions lighthouse-core/report/html/renderer/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ class Util {
category.auditRefs.forEach(auditMeta => {
const result = clone.audits[auditMeta.id];
auditMeta.result = result;

// attach the stackpacks to the auditRef object
if (clone.stackPacks) {
wardpeet marked this conversation as resolved.
Show resolved Hide resolved
clone.stackPacks.forEach(pack => {
if (pack.descriptions[auditMeta.id]) {
auditMeta.result.stackPacks = auditMeta.result.stackPacks || [];
auditMeta.result.stackPacks.push({
title: pack.title,
iconDataURL: pack.iconDataURL,
description: pack.descriptions[auditMeta.id],
});
}
});
}
});
}

Expand Down
16 changes: 13 additions & 3 deletions lighthouse-core/report/html/report-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
color: #0c50c7;
}

.lh-audit__description {
.lh-audit__description, .lh-audit__stackpack {
--inner-audit-left-padding: calc(var(--text-indent) + var(--lh-audit-index-width) + 2 * var(--audit-item-gap));
--inner-audit-right-padding: calc(var(--text-indent) + 2px);
padding-left: var(--inner-audit-left-padding);
Expand All @@ -192,6 +192,16 @@
max-width: 70%;
}

.lh-audit__stackpack {
display: flex;
align-items: center;
}

.lh-audit__stackpack__img {
max-width: 50px;
margin-right: var(--default-padding)
}

/* Report header */

.report-icon {
Expand Down Expand Up @@ -276,10 +286,10 @@
}

.lh-category-header__description,
.lh-audit__description {
.lh-audit__description,
.lh-audit__stackpack {
color: var(--secondary-text-color);
}

.lh-category-header__description {
font-size: var(--body-font-size);
margin: calc(var(--default-padding) / 2) 0 var(--default-padding);
Expand Down
4 changes: 3 additions & 1 deletion lighthouse-core/report/html/templates.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
</div>
</summary>
<div class="lh-audit__description"></div>
<div class="lh-audit__stackpacks"></div>
</details>
</div>
</template>
Expand Down Expand Up @@ -117,6 +118,7 @@
</div>
</summary>
<div class="lh-audit__description"></div>
<div class="lh-audit__stackpacks"></div>
</details>
</div>
</template>
Expand Down Expand Up @@ -453,7 +455,7 @@
<div class="after"></div>
</div>
</div>


<div class="lh-header-container">
<div class="lh-header">
Expand Down
Loading