Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2df023e
Add files to gitignore.
sebastian9er Nov 15, 2024
f95d133
Merge remote-tracking branch 'upstream/main'
sebastian9er Nov 15, 2024
e52e1b8
Merge branch 'GoogleChrome:main' into main
sebastian9er Nov 27, 2024
3861727
Merge branch 'main' of https://github.com/sebastian9er/lighthouse
sebastian9er Dec 4, 2024
8717e3c
Commit gitignore
sebastian9er Dec 4, 2024
009af4e
Merge branch 'GoogleChrome:main' into main
sebastian9er Dec 12, 2024
b244376
Merge branch 'main' of https://github.com/sebastian9er/lighthouse
sebastian9er Dec 13, 2024
47f4de5
Merge branch 'main' of https://github.com/sebastian9er/lighthouse
sebastian9er Jan 8, 2025
adadeca
Merge branch 'main' of https://github.com/sebastian9er/lighthouse
sebastian9er Feb 18, 2025
3e41e00
Merge branch 'trusted-types' of https://github.com/sebastian9er/light…
sebastian9er May 8, 2025
f908dd6
Merge branch 'main' of https://github.com/sebastian9er/lighthouse
sebastian9er May 8, 2025
cc6c040
Initial version of the Trusted Types audit for Lighthouse.
sebastian9er May 16, 2025
ee508d7
Re-adding yarn.lock
sebastian9er May 16, 2025
da443d8
Fixing up yarn.lock (probably for the first time of many).
sebastian9er May 16, 2025
9424b22
Fix up gitignore to stop complaining.
sebastian9er May 19, 2025
491964a
Smokehouse tests for the trusted types Lighthouse audit.
sebastian9er May 20, 2025
e900a6b
Update docs for trusted types Lighthouse audit.
sebastian9er May 20, 2025
b08dd34
Merge branch 'GoogleChrome:main' into trusted-types
sebastian9er May 28, 2025
fe7e350
Fix date in copyright.
sebastian9er May 28, 2025
979136c
Merge branch 'GoogleChrome:main' into trusted-types
sebastian9er Jun 6, 2025
4a25028
Fix description, TODO link and include metadata for evaluation.
sebastian9er Jun 18, 2025
c42f57c
Update json files.
sebastian9er Jun 18, 2025
aef0048
Fix tests.
sebastian9er Jun 18, 2025
8505587
Fix package.json to make PR workflows happy. Hopefully.
sebastian9er Jun 19, 2025
cee8d73
Merge branch 'main' into trusted-types
sebastian9er Jun 19, 2025
5ce1dd8
Reverting changes to shared/localization/locales as requested in the PR.
sebastian9er Jun 23, 2025
29ccd04
Fix gitignore and package.json
sebastian9er Jun 25, 2025
8e477c1
Merge remote
sebastian9er Jun 25, 2025
9610cbc
Fix sample flow and sample json
sebastian9er Jun 25, 2025
bfd2fb8
Merge branch 'main' into trusted-types
sebastian9er Jun 25, 2025
77c5638
Clean checkout, fix yarn, fix lighthouse link and check in locales.
sebastian9er Jun 25, 2025
70657a0
Lint fix
sebastian9er Jun 25, 2025
fff82a5
Adding yarn.lock back in.
sebastian9er Jul 1, 2025
476cbc1
Fix sample flow file.
sebastian9er Jul 1, 2025
5165f95
Fix UIStrings.
sebastian9er Jul 1, 2025
ff40967
Merge remote-tracking branch 'upstream/main'
sebastian9er Jul 3, 2025
2923ce0
Merge branch 'main' into trusted-types
sebastian9er Jul 3, 2025
4bc0ec8
Fix linter and update json.
sebastian9er Jul 3, 2025
aa94cfa
Add smokehouse-bin back in.
sebastian9er Jul 4, 2025
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
4 changes: 4 additions & 0 deletions cli/test/smokehouse/core-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import serviceWorkerReloaded from './test-definitions/service-worker-reloaded.js
import shiftAttribution from './test-definitions/shift-attribution.js';
import sourceMaps from './test-definitions/source-maps.js';
import timing from './test-definitions/timing.js';
import trustedTypesDirectivePresent from './test-definitions/trusted-types-directive-present.js';
import trustedTypesDirectiveMissingDirective from './test-definitions/trusted-types-missing-directives.js';

/** @type {ReadonlyArray<Smokehouse.TestDfn>} */
const smokeTests = [
Expand Down Expand Up @@ -129,6 +131,8 @@ const smokeTests = [
shiftAttribution,
sourceMaps,
timing,
trustedTypesDirectivePresent,
trustedTypesDirectiveMissingDirective,
];

export default smokeTests;
Empty file modified cli/test/smokehouse/frontends/smokehouse-bin.js
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @type {Smokehouse.ExpectedRunnerResult}
* Expected Lighthouse results for a site with present DOM-XSS mitigations
* (through a Trusted-Types direcive in the Content-Security-Policy header).
*/
const expectations = {
lhr: {
requestedUrl: 'https://m.youtube.com/',
finalDisplayedUrl: 'https://m.youtube.com/',
audits: {
'trusted-types-xss': {
score: null,
},
},
},
};

export default {
id: 'trusted-types-directive-present',
expectations,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @type {Smokehouse.ExpectedRunnerResult}
* Expected Lighthouse results for a site with missing DOM-XSS mitigations
* (through the lack of Trusted-Types direcives in the Content-Security-Policy
* headers).
*/
const expectations = {
lhr: {
requestedUrl: 'https://example.com/',
finalDisplayedUrl: 'https://example.com/',
audits: {
'trusted-types-xss': {
score: 1,
details: {
items: [
{
description: 'No Content-Security-Policy header with Trusted Types directive found',
severity: 'High',
},
],
},
},
},
},
};

export default {
id: 'trusted-types-missing-directives',
expectations,
};
122 changes: 122 additions & 0 deletions core/audits/trusted-types-xss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import {Audit} from './audit.js';
import {MainResource} from '../computed/main-resource.js';
import * as i18n from '../lib/i18n/i18n.js';

const UIStrings = {
/** Title of a Lighthouse audit that evaluates whether the set CSP header and Trusted Types directive is mitigating DOM-based XSS. "CSP" stands for "Content-Security-Policy" and should not be translated. "XSS" stands for "Cross Site Scripting" and should not be translated. */
title: 'Mitigate DOM-based XSS with Trusted Types',
/** Description of a Lighthouse audit that evaluates whether the set CSP header and Trusted Types directive is mitigating DOM-based XSS. This is displayed after a user expands the section to see more. "CSP" stands for "Content-Security-Policy" and should not be translated. "XSS" stands for "Cross Site Scripting" and should not be translated. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
description: 'The `require-trusted-types-for` directive in the `Content-Security-Policy` (CSP) header instructs user agents to control the data passed to DOM XSS sink functions. [Learn more about mitigating DOM-based XSS](https://developer.chrome.com/docs/lighthouse/best-practices/TODO).',
/** Summary text for the results of a Lighthouse audit that evaluates whether the set CSP header and Trusted Types directive is mitigating DOM-based XSS. This text is displayed if the page does not respond with a CSP header and a Trusted Types directive. "CSP" stands for "Content-Security-Policy" and should not be translated. "XSS" stands for "Cross Site Scripting" and should not be translated. */
noTrustedTypesToMitigateXss: 'No Content-Security-Policy header with Trusted Types directive found',
/** Label for a column in a data table; entries will be the severity of an issue with the page's CSP and Trusted Types directive. */
columnSeverity: 'Severity',
};

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

class TrustedTypesXss extends Audit {
/**
* @return {LH.Audit.Meta}
*/
static get meta() {
return {
id: 'trusted-types-xss',
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
title: str_(UIStrings.title),
description: str_(UIStrings.description),
requiredArtifacts: ['DevtoolsLog', 'URL'],
supportedModes: ['navigation'],
};
}

/**
* @param {LH.Artifacts} artifacts
* @param {LH.Audit.Context} context
* @return {Promise<string[]>}
*/
static async getRawCsp(artifacts, context) {
const devtoolsLog = artifacts.DevtoolsLog;
const mainResource =
await MainResource.request({devtoolsLog, URL: artifacts.URL}, context);

const cspHeaders = mainResource.responseHeaders
.filter(h => {
return h.name.toLowerCase() === 'content-security-policy';
})
.flatMap(h => h.value.split(','))
.filter(rawCsp => rawCsp.replace(/\s/g, ''));

return cspHeaders;
}

/**
* @param {LH.IcuMessage | string} findingDescription
* @param {LH.IcuMessage=} severity
* @return {LH.Audit.Details.TableItem}
*/
static findingToTableItem(findingDescription, severity) {
return {
description: findingDescription,
severity,
};
}

/**
* @param {string[]} cspHeaders
* @return {{score: number, results: LH.Audit.Details.TableItem[]}}
*/
static constructResults(cspHeaders) {
// Check for require-trusted-types-for 'script' in CSP.
for (const cspHeader of cspHeaders) {
for (const directive of cspHeader.split(';')) {
if (directive.includes('require-trusted-types-for') &&
directive.includes('script')) {
return {score: 1, results: []};
}
}
}

return {
score: 0,
results: [{
severity: str_(i18n.UIStrings.itemSeverityHigh),
description: str_(UIStrings.noTrustedTypesToMitigateXss),
}],
};
}

/**
* @param {LH.Artifacts} artifacts
* @param {LH.Audit.Context} context
* @return {Promise<LH.Audit.Product>}
*/
static async audit(artifacts, context) {
const cspHeaders = await this.getRawCsp(artifacts, context);
const {score, results} = this.constructResults(cspHeaders);

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
/* eslint-disable max-len */
{key: 'description', valueType: 'text', subItemsHeading: {key: 'description'}, label: str_(i18n.UIStrings.columnDescription)},
{key: 'severity', valueType: 'text', subItemsHeading: {key: 'severity'}, label: str_(UIStrings.columnSeverity)},
/* eslint-enable max-len */
];
const details = Audit.makeTableDetails(headings, results);

return {
score,
notApplicable: !results.length,
details,
};
}
}

export default TrustedTypesXss;
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 @@ -198,6 +198,7 @@ const defaultConfig = {
'has-hsts',
'origin-isolation',
'clickjacking-mitigation',
'trusted-types-xss',
'script-treemap-data',
'accessibility/accesskeys',
'accessibility/aria-allowed-attr',
Expand Down Expand Up @@ -590,6 +591,7 @@ const defaultConfig = {
{id: 'has-hsts', weight: 0, group: 'best-practices-trust-safety'},
{id: 'origin-isolation', weight: 0, group: 'best-practices-trust-safety'},
{id: 'clickjacking-mitigation', weight: 0, group: 'best-practices-trust-safety'},
{id: 'trusted-types-xss', weight: 0, group: 'best-practices-trust-safety'},
// User Experience
{id: 'paste-preventing-inputs', weight: 3, group: 'best-practices-ux'},
{id: 'image-aspect-ratio', weight: 1, group: 'best-practices-ux'},
Expand Down
1 change: 1 addition & 0 deletions core/scripts/i18n/collect-strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ function checkKnownFixedCollisions(strings) {
'Severity',
'Severity',
'Severity',
'Severity',
'Total',
'Total',
'Use $MARKDOWN_SNIPPET_0$ to detect unused JavaScript code. $LINK_START_0$Learn more$LINK_END_0$',
Expand Down
Loading
Loading