lighthouse 12.7.1-dev.20250705 → 12.7.1-dev.20250707
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/test/smokehouse/core-tests.js +4 -0
- package/core/audits/trusted-types-xss.d.ts +41 -0
- package/core/audits/trusted-types-xss.js +137 -0
- package/core/config/default-config.js +2 -0
- package/package.json +1 -1
- package/shared/localization/locales/en-US.json +12 -0
- package/shared/localization/locales/en-XL.json +12 -0
|
@@ -66,6 +66,8 @@ import serviceWorkerReloaded from './test-definitions/service-worker-reloaded.js
|
|
|
66
66
|
import shiftAttribution from './test-definitions/shift-attribution.js';
|
|
67
67
|
import sourceMaps from './test-definitions/source-maps.js';
|
|
68
68
|
import timing from './test-definitions/timing.js';
|
|
69
|
+
import trustedTypesDirectivePresent from './test-definitions/trusted-types-directive-present.js';
|
|
70
|
+
import trustedTypesDirectiveMissingDirective from './test-definitions/trusted-types-missing-directives.js';
|
|
69
71
|
|
|
70
72
|
/** @type {ReadonlyArray<Smokehouse.TestDfn>} */
|
|
71
73
|
const smokeTests = [
|
|
@@ -131,6 +133,8 @@ const smokeTests = [
|
|
|
131
133
|
shiftAttribution,
|
|
132
134
|
sourceMaps,
|
|
133
135
|
timing,
|
|
136
|
+
trustedTypesDirectivePresent,
|
|
137
|
+
trustedTypesDirectiveMissingDirective,
|
|
134
138
|
];
|
|
135
139
|
|
|
136
140
|
export default smokeTests;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export default TrustedTypesXss;
|
|
2
|
+
declare class TrustedTypesXss extends Audit {
|
|
3
|
+
/**
|
|
4
|
+
* @param {LH.Artifacts} artifacts
|
|
5
|
+
* @param {LH.Audit.Context} context
|
|
6
|
+
* @return {Promise<{cspHeaders: string[], cspMetaTags: string[]}>}
|
|
7
|
+
*/
|
|
8
|
+
static getRawCsps(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<{
|
|
9
|
+
cspHeaders: string[];
|
|
10
|
+
cspMetaTags: string[];
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* @param {LH.IcuMessage | string} findingDescription
|
|
14
|
+
* @param {LH.IcuMessage=} severity
|
|
15
|
+
* @return {LH.Audit.Details.TableItem}
|
|
16
|
+
*/
|
|
17
|
+
static findingToTableItem(findingDescription: LH.IcuMessage | string, severity?: LH.IcuMessage | undefined): LH.Audit.Details.TableItem;
|
|
18
|
+
/**
|
|
19
|
+
* @param {string[]} cspHeaders
|
|
20
|
+
* @param {string[]} cspMetaTags
|
|
21
|
+
* @return {{score: number, results: LH.Audit.Details.TableItem[]}}
|
|
22
|
+
*/
|
|
23
|
+
static constructResults(cspHeaders: string[], cspMetaTags: string[]): {
|
|
24
|
+
score: number;
|
|
25
|
+
results: LH.Audit.Details.TableItem[];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* @param {LH.Artifacts} artifacts
|
|
29
|
+
* @param {LH.Audit.Context} context
|
|
30
|
+
* @return {Promise<LH.Audit.Product>}
|
|
31
|
+
*/
|
|
32
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
33
|
+
}
|
|
34
|
+
export namespace UIStrings {
|
|
35
|
+
let title: string;
|
|
36
|
+
let description: string;
|
|
37
|
+
let noTrustedTypesToMitigateXss: string;
|
|
38
|
+
let columnSeverity: string;
|
|
39
|
+
}
|
|
40
|
+
import { Audit } from './audit.js';
|
|
41
|
+
//# sourceMappingURL=trusted-types-xss.d.ts.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {Directive} from 'csp_evaluator/dist/csp.js';
|
|
8
|
+
|
|
9
|
+
import {Audit} from './audit.js';
|
|
10
|
+
import {MainResource} from '../computed/main-resource.js';
|
|
11
|
+
import * as i18n from '../lib/i18n/i18n.js';
|
|
12
|
+
import {parseCsp} from '../lib/csp-evaluator.js';
|
|
13
|
+
|
|
14
|
+
const UIStrings = {
|
|
15
|
+
/** 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. */
|
|
16
|
+
title: 'Mitigate DOM-based XSS with Trusted Types',
|
|
17
|
+
/** 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. */
|
|
18
|
+
description:
|
|
19
|
+
'The `require-trusted-types-for` directive in the `Content-Security-Policy` (CSP) header ' +
|
|
20
|
+
'instructs user agents to control the data passed to DOM XSS sink functions. ' +
|
|
21
|
+
'[Learn more about mitigating DOM-based XSS with Trusted Types](https://web.dev/articles/trusted-types).',
|
|
22
|
+
/** 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. */
|
|
23
|
+
noTrustedTypesToMitigateXss:
|
|
24
|
+
'No `Content-Security-Policy` header with Trusted Types directive found',
|
|
25
|
+
/** 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. */
|
|
26
|
+
columnSeverity: 'Severity',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
30
|
+
|
|
31
|
+
class TrustedTypesXss extends Audit {
|
|
32
|
+
/**
|
|
33
|
+
* @return {LH.Audit.Meta}
|
|
34
|
+
*/
|
|
35
|
+
static get meta() {
|
|
36
|
+
return {
|
|
37
|
+
id: 'trusted-types-xss',
|
|
38
|
+
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
|
|
39
|
+
title: str_(UIStrings.title),
|
|
40
|
+
description: str_(UIStrings.description),
|
|
41
|
+
requiredArtifacts: ['DevtoolsLog', 'MetaElements', 'URL'],
|
|
42
|
+
supportedModes: ['navigation'],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {LH.Artifacts} artifacts
|
|
48
|
+
* @param {LH.Audit.Context} context
|
|
49
|
+
* @return {Promise<{cspHeaders: string[], cspMetaTags: string[]}>}
|
|
50
|
+
*/
|
|
51
|
+
static async getRawCsps(artifacts, context) {
|
|
52
|
+
const devtoolsLog = artifacts.DevtoolsLog;
|
|
53
|
+
const mainResource = await MainResource.request({devtoolsLog, URL: artifacts.URL}, context);
|
|
54
|
+
|
|
55
|
+
const cspMetaTags = artifacts.MetaElements
|
|
56
|
+
.filter(m => {
|
|
57
|
+
return m.httpEquiv && m.httpEquiv.toLowerCase() === 'content-security-policy';
|
|
58
|
+
})
|
|
59
|
+
.flatMap(m => (m.content || '').split(','))
|
|
60
|
+
.filter(rawCsp => rawCsp.replace(/\s/g, ''));
|
|
61
|
+
const cspHeaders = mainResource.responseHeaders
|
|
62
|
+
.filter(h => {
|
|
63
|
+
return h.name.toLowerCase() === 'content-security-policy';
|
|
64
|
+
})
|
|
65
|
+
.flatMap(h => h.value.split(','))
|
|
66
|
+
.filter(rawCsp => rawCsp.replace(/\s/g, ''));
|
|
67
|
+
|
|
68
|
+
return {cspHeaders, cspMetaTags};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {LH.IcuMessage | string} findingDescription
|
|
73
|
+
* @param {LH.IcuMessage=} severity
|
|
74
|
+
* @return {LH.Audit.Details.TableItem}
|
|
75
|
+
*/
|
|
76
|
+
static findingToTableItem(findingDescription, severity) {
|
|
77
|
+
return {
|
|
78
|
+
description: findingDescription,
|
|
79
|
+
severity,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {string[]} cspHeaders
|
|
85
|
+
* @param {string[]} cspMetaTags
|
|
86
|
+
* @return {{score: number, results: LH.Audit.Details.TableItem[]}}
|
|
87
|
+
*/
|
|
88
|
+
static constructResults(cspHeaders, cspMetaTags) {
|
|
89
|
+
const rawCsps = [...cspHeaders, ...cspMetaTags];
|
|
90
|
+
const parsedCsps = rawCsps.map(parseCsp);
|
|
91
|
+
|
|
92
|
+
// Check for require-trusted-types-for 'script' in CSP.
|
|
93
|
+
for (const pc of parsedCsps) {
|
|
94
|
+
const directiveValues = pc.directives[pc.getEffectiveDirective(
|
|
95
|
+
Directive.REQUIRE_TRUSTED_TYPES_FOR)] || [];
|
|
96
|
+
if (directiveValues.includes('\'script\'')) {
|
|
97
|
+
return {score: 1, results: []};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
score: 0,
|
|
103
|
+
results: [{
|
|
104
|
+
severity: str_(i18n.UIStrings.itemSeverityHigh),
|
|
105
|
+
description: str_(UIStrings.noTrustedTypesToMitigateXss),
|
|
106
|
+
}],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {LH.Artifacts} artifacts
|
|
112
|
+
* @param {LH.Audit.Context} context
|
|
113
|
+
* @return {Promise<LH.Audit.Product>}
|
|
114
|
+
*/
|
|
115
|
+
static async audit(artifacts, context) {
|
|
116
|
+
const {cspHeaders, cspMetaTags} = await this.getRawCsps(artifacts, context);
|
|
117
|
+
const {score, results} = this.constructResults(cspHeaders, cspMetaTags);
|
|
118
|
+
|
|
119
|
+
/** @type {LH.Audit.Details.Table['headings']} */
|
|
120
|
+
const headings = [
|
|
121
|
+
/* eslint-disable max-len */
|
|
122
|
+
{key: 'description', valueType: 'text', subItemsHeading: {key: 'description'}, label: str_(i18n.UIStrings.columnDescription)},
|
|
123
|
+
{key: 'severity', valueType: 'text', subItemsHeading: {key: 'severity'}, label: str_(UIStrings.columnSeverity)},
|
|
124
|
+
/* eslint-enable max-len */
|
|
125
|
+
];
|
|
126
|
+
const details = Audit.makeTableDetails(headings, results);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
score,
|
|
130
|
+
notApplicable: !results.length,
|
|
131
|
+
details,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default TrustedTypesXss;
|
|
137
|
+
export {UIStrings};
|
|
@@ -198,6 +198,7 @@ const defaultConfig = {
|
|
|
198
198
|
'has-hsts',
|
|
199
199
|
'origin-isolation',
|
|
200
200
|
'clickjacking-mitigation',
|
|
201
|
+
'trusted-types-xss',
|
|
201
202
|
'script-treemap-data',
|
|
202
203
|
'accessibility/accesskeys',
|
|
203
204
|
'accessibility/aria-allowed-attr',
|
|
@@ -590,6 +591,7 @@ const defaultConfig = {
|
|
|
590
591
|
{id: 'has-hsts', weight: 0, group: 'best-practices-trust-safety'},
|
|
591
592
|
{id: 'origin-isolation', weight: 0, group: 'best-practices-trust-safety'},
|
|
592
593
|
{id: 'clickjacking-mitigation', weight: 0, group: 'best-practices-trust-safety'},
|
|
594
|
+
{id: 'trusted-types-xss', weight: 0, group: 'best-practices-trust-safety'},
|
|
593
595
|
// User Experience
|
|
594
596
|
{id: 'paste-preventing-inputs', weight: 3, group: 'best-practices-ux'},
|
|
595
597
|
{id: 'image-aspect-ratio', weight: 1, group: 'best-practices-ux'},
|
package/package.json
CHANGED
|
@@ -1493,6 +1493,18 @@
|
|
|
1493
1493
|
"core/audits/third-party-summary.js | title": {
|
|
1494
1494
|
"message": "Minimize third-party usage"
|
|
1495
1495
|
},
|
|
1496
|
+
"core/audits/trusted-types-xss.js | columnSeverity": {
|
|
1497
|
+
"message": "Severity"
|
|
1498
|
+
},
|
|
1499
|
+
"core/audits/trusted-types-xss.js | description": {
|
|
1500
|
+
"message": "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 with Trusted Types](https://web.dev/articles/trusted-types)."
|
|
1501
|
+
},
|
|
1502
|
+
"core/audits/trusted-types-xss.js | noTrustedTypesToMitigateXss": {
|
|
1503
|
+
"message": "No `Content-Security-Policy` header with Trusted Types directive found"
|
|
1504
|
+
},
|
|
1505
|
+
"core/audits/trusted-types-xss.js | title": {
|
|
1506
|
+
"message": "Mitigate DOM-based XSS with Trusted Types"
|
|
1507
|
+
},
|
|
1496
1508
|
"core/audits/unsized-images.js | description": {
|
|
1497
1509
|
"message": "Set an explicit width and height on image elements to reduce layout shifts and improve CLS. [Learn how to set image dimensions](https://web.dev/articles/optimize-cls#images_without_dimensions)"
|
|
1498
1510
|
},
|
|
@@ -1493,6 +1493,18 @@
|
|
|
1493
1493
|
"core/audits/third-party-summary.js | title": {
|
|
1494
1494
|
"message": "M̂ín̂ím̂íẑé t̂h́îŕd̂-ṕâŕt̂ý ûśâǵê"
|
|
1495
1495
|
},
|
|
1496
|
+
"core/audits/trusted-types-xss.js | columnSeverity": {
|
|
1497
|
+
"message": "Ŝév̂ér̂ít̂ý"
|
|
1498
|
+
},
|
|
1499
|
+
"core/audits/trusted-types-xss.js | description": {
|
|
1500
|
+
"message": "T̂h́ê `require-trusted-types-for` d́îŕêćt̂ív̂é îń t̂h́ê `Content-Security-Policy` (ĆŜṔ) ĥéâd́êŕ îńŝt́r̂úĉt́ŝ úŝér̂ áĝén̂t́ŝ t́ô ćôńt̂ŕôĺ t̂h́ê d́ât́â ṕâśŝéd̂ t́ô D́ÔḾ X̂ŚŜ śîńk̂ f́ûńĉt́îón̂ś. [L̂éâŕn̂ ḿôŕê áb̂óût́ m̂ít̂íĝát̂ín̂ǵ D̂ÓM̂-b́âśêd́ X̂ŚŜ ẃît́ĥ T́r̂úŝt́êd́ T̂ýp̂éŝ](https://web.dev/articles/trusted-types)."
|
|
1501
|
+
},
|
|
1502
|
+
"core/audits/trusted-types-xss.js | noTrustedTypesToMitigateXss": {
|
|
1503
|
+
"message": "N̂ó `Content-Security-Policy` ĥéâd́êŕ ŵít̂h́ T̂ŕûśt̂éd̂ T́ŷṕêś d̂ír̂éĉt́îv́ê f́ôún̂d́"
|
|
1504
|
+
},
|
|
1505
|
+
"core/audits/trusted-types-xss.js | title": {
|
|
1506
|
+
"message": "M̂ít̂íĝát̂é D̂ÓM̂-b́âśêd́ X̂ŚŜ ẃît́ĥ T́r̂úŝt́êd́ T̂ýp̂éŝ"
|
|
1507
|
+
},
|
|
1496
1508
|
"core/audits/unsized-images.js | description": {
|
|
1497
1509
|
"message": "Ŝét̂ án̂ éx̂ṕl̂íĉít̂ ẃîd́t̂h́ âńd̂ h́êíĝh́t̂ ón̂ ím̂áĝé êĺêḿêńt̂ś t̂ó r̂éd̂úĉé l̂áŷóût́ ŝh́îf́t̂ś âńd̂ ím̂ṕr̂óv̂é ĈĹŜ. [Ĺêár̂ń ĥóŵ t́ô śêt́ îḿâǵê d́îḿêńŝíôńŝ](https://web.dev/articles/optimize-cls#images_without_dimensions)"
|
|
1498
1510
|
},
|