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

new_audit: third-party cookies #15632

Merged
merged 14 commits into from
Dec 8, 2023
22 changes: 22 additions & 0 deletions cli/test/fixtures/dobetterweb/dbw_tester.html
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,24 @@ <h2 id="toppy" style="background-image:url('');">Do better web tester page</h2>
});
}

function thirdPartyCookieTest() {
const cookieInlineParam = headersParam([[
'Set-Cookie',
'Foo=Bar;SameSite=None;Secure;',
]]);
const script = document.createElement('script');
// Use [::1] instead of localhost so this shows up as third party
script.src = 'http://[::1]:10503/dobetterweb/empty_module.js?' + cookieInlineParam;
Fixed Show fixed Hide fixed
requestAnimationFrame(() => {
document.body.append(script);
});
}

function headersParam(headers) {
const headerString = new URLSearchParams(headers).toString();
return new URLSearchParams([['extra_header', headerString]]).toString();
}

// Figure out which tests to fun.
const params = new URLSearchParams(location.search);
if (location.search === '') {
Expand All @@ -422,6 +440,7 @@ <h2 id="toppy" style="background-image:url('');">Do better web tester page</h2>
inputsCanBePastedIntoTest();
isOnHttps();
noUnloadListenersTest();
thirdPartyCookieTest();
} else {
if (params.has('documentWrite')) {
documentWriteTest();
Expand Down Expand Up @@ -459,6 +478,9 @@ <h2 id="toppy" style="background-image:url('');">Do better web tester page</h2>
if (params.has('isonhttps')) {
isOnHttps();
}
if (params.has('3pcookie')) {
thirdPartyCookieTest();
}
}
</script>

Expand Down
2 changes: 1 addition & 1 deletion cli/test/fixtures/static-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import esMain from 'es-main';

import {LH_ROOT} from '../../../shared/root.js';

const HEADER_SAFELIST = new Set(['x-robots-tag', 'link', 'content-security-policy']);
const HEADER_SAFELIST = new Set(['x-robots-tag', 'link', 'content-security-policy', 'set-cookie']);
const wasInvokedDirectly = esMain(import.meta);

class Server {
Expand Down
15 changes: 13 additions & 2 deletions cli/test/smokehouse/test-definitions/dobetterweb.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,15 +448,15 @@ const expectations = {
},
'dom-size': {
score: null,
numericValue: 153,
numericValue: 154,
details: {
items: [
{
statistic: 'Total DOM Elements',
value: {
type: 'numeric',
granularity: 1,
value: 153,
value: 154,
},
},
{
Expand Down Expand Up @@ -553,6 +553,7 @@ const expectations = {
'network-rtt': {
details: {
items: [
{origin: 'http://[::1]:10503', rtt: '>0'},
{origin: 'http://localhost:10200', rtt: '>0'},
],
},
Expand All @@ -561,6 +562,7 @@ const expectations = {
details: {
items: [
{origin: 'http://localhost:10200', serverResponseTime: '>0'},
{origin: 'http://[::1]:10503', serverResponseTime: '>0'},
],
},
},
Expand Down Expand Up @@ -598,6 +600,15 @@ const expectations = {
],
},
},
'third-party-cookies': {
score: 0,
displayValue: '1 cookie found',
details: {
items: [
{name: 'Foo', url: /^http:\/\/\[::1\]:10503\/dobetterweb\/empty_module\.js/},
Copy link
Collaborator

@connorjclark connorjclark Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should make a literalToRegexStartsWith('https://.....') ....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new RegExp will escape all of the / for us but not the [. Eh too much thinking

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

],
},
},
},
fullPageScreenshot: {
screenshot: {
Expand Down
112 changes: 112 additions & 0 deletions core/audits/third-party-cookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @fileoverview Audits a page to determine if it is using third party cookies.
*/

import {Audit} from './audit.js';
import * as i18n from '../lib/i18n/i18n.js';

/* eslint-disable max-len */
const UIStrings = {
/** Title of a Lighthouse audit that provides detail on the use of third party cookies. This descriptive title is shown to users when the page does not use third party cookies. */
title: 'Avoids third-party cookies',
/** Title of a Lighthouse audit that provides detail on the use of third party cookies. This descriptive title is shown to users when the page uses third party cookies. */
failureTitle: 'Uses third-party cookies',
/** Description of a Lighthouse audit that tells the user why they should not use third party cookies on their page. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
description: 'Support for third-party cookies will be removed in a future version of Chrome. [Learn more about phasing out third-party cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/).',
/** [ICU Syntax] Label for the audit identifying the number of third-party cookies. */
displayValue: `{itemCount, plural,
=1 {1 cookie found}
other {# cookies found}
}`,
};
/* eslint-enable max-len */

const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);

class ThirdPartyCookies extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'third-party-cookies',
title: str_(UIStrings.title),
failureTitle: str_(UIStrings.failureTitle),
description: str_(UIStrings.description),
requiredArtifacts: ['InspectorIssues'],
};
}

/**
* https://source.chromium.org/chromium/chromium/src/+/d2fcd4ba302baeabf4b96d8fa9fdb7a215736c31:third_party/devtools-frontend/src/front_end/models/issues_manager/CookieIssue.ts;l=62-69
* @param {LH.Crdp.Audits.CookieIssueDetails} cookieIssue
* @return {string}
*/
static getCookieId(cookieIssue) {
if (!cookieIssue.cookie) {
return cookieIssue.rawCookieLine ?? 'no-cookie-info';
}

const {domain, path, name} = cookieIssue.cookie;
const cookieId = `${domain};${path};${name}`;
return cookieId;
}

/**
* @param {LH.Artifacts} artifacts
* @return {Promise<LH.Audit.Product>}
*/
static async audit(artifacts) {
/** @type {Set<string>} */
const seenCookies = new Set();

/** @type {LH.Audit.Details.TableItem[]} */
const items = [];
for (const issue of artifacts.InspectorIssues.cookieIssue) {
const isPhaseoutWarn = issue.cookieWarningReasons.includes('WarnThirdPartyPhaseout');
const isPhaseoutExclude = issue.cookieExclusionReasons.includes('ExcludeThirdPartyPhaseout');
if (!isPhaseoutWarn && !isPhaseoutExclude) continue;

// According to JSDOC for `issue.cookie`, if `cookie` is undefined then `rawCookieLine`
// should be set and no valid cookie could be created. It should be safe to skip in this case.
const name = issue.cookie?.name || issue.rawCookieLine;
if (!name) continue;

const cookieId = this.getCookieId(issue);
if (seenCookies.has(cookieId)) continue;
seenCookies.add(cookieId);

items.push({
name,
url: issue.cookieUrl,
});
}

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
{key: 'name', valueType: 'text', label: str_(i18n.UIStrings.columnName)},
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
];
const details = Audit.makeTableDetails(headings, items);

let displayValue;
if (items.length > 0) {
displayValue = str_(UIStrings.displayValue, {itemCount: items.length});
}

return {
score: items.length ? 0 : 1,
displayValue,
details,
};
}
}

export default ThirdPartyCookies;
export {UIStrings};
2 changes: 2 additions & 0 deletions core/config/default-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const defaultConfig = {
'image-size-responsive',
'preload-fonts',
'deprecations',
'third-party-cookies',
'mainthread-work-breakdown',
'bootup-time',
'uses-rel-preload',
Expand Down Expand Up @@ -607,6 +608,7 @@ const defaultConfig = {
{id: 'no-unload-listeners', weight: 1, group: 'best-practices-general'},
{id: 'js-libraries', weight: 0, group: 'best-practices-general'},
{id: 'deprecations', weight: 5, group: 'best-practices-general'},
{id: 'third-party-cookies', weight: 5, group: 'best-practices-general'},
{id: 'errors-in-console', weight: 1, group: 'best-practices-general'},
{id: 'valid-source-maps', weight: 0, group: 'best-practices-general'},
{id: 'inspector-issues', weight: 1, group: 'best-practices-general'},
Expand Down
Loading
Loading