Skip to content

Commit c9c6706

Browse files
authored
Validate meta CSP directives (#107)
1 parent 459c902 commit c9c6706

File tree

7 files changed

+105
-15
lines changed

7 files changed

+105
-15
lines changed

crx/capo.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crx/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 3,
33
"name": "Capo: get your ﹤𝚑𝚎𝚊𝚍﹥ in order",
44
"description": "Visualize the optimal ordering of ﹤𝚑𝚎𝚊𝚍﹥ elements on any web page",
5-
"version": "1.5.0",
5+
"version": "1.5.1",
66
"permissions": [
77
"scripting",
88
"activeTab",

docs/src/content/docs/user/validation.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ In the example above, there is a `<meta>` CSP element, so capo.js warns that "CS
116116

117117
This validation warning is an example of capo.js being more opinionated than simply following the specification. The warning includes a recommendation to use the CSP header instead, which avoids the preload scanner issue all together. Also note that the `Content-Security-Policy-Report-Only` directive is only valid as an HTTP header and not as a `<meta http-equiv>` element.
118118

119+
Additionally, some CSP directives are not allowed in the `<meta>` declaration:
120+
121+
- `frame-ancestors`
122+
- `sandbox`
123+
- `report-uri`
124+
125+
If capo.js detects any of these directives in a `<meta>` CSP element, it will log a validation warning.
126+
119127
### No invalid origin trials
120128

121129
Sites can register for [origin trials](https://developer.chrome.com/en/docs/web-platform/origin-trials/) to enable individual experimental web platform features. To enable them on a given site, a token must be included as either an `Origin-Trial` HTTP header or `<meta http-equiv>` element.

docs/src/lib/capo.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,11 +540,36 @@ function $c322f9a5057eaf5c$export$6c93e2175c028eeb(element) {
540540
}
541541
function $c322f9a5057eaf5c$var$validateCSP(element) {
542542
const warnings = [];
543-
if (element.matches('meta[http-equiv="Content-Security-Policy-Report-Only" i]')) //https://w3c.github.io/webappsec-csp/#meta-element
544-
warnings.push("CSP Report-Only is forbidden in meta tags");
545-
else if (element.matches('meta[http-equiv="Content-Security-Policy" i]')) warnings.push("meta CSP discouraged. See https://crbug.com/1458493.");
543+
let payload = null;
544+
if (element.matches('meta[http-equiv="Content-Security-Policy-Report-Only" i]')) {
545+
//https://w3c.github.io/webappsec-csp/#meta-element
546+
warnings.push("CSP Report-Only is forbidden in meta tags");
547+
return warnings;
548+
}
549+
if (element.matches('meta[http-equiv="Content-Security-Policy" i]')) warnings.push("meta CSP discouraged. See https://crbug.com/1458493.");
550+
const content = element.getAttribute("content");
551+
if (!content) {
552+
warnings.push("Invalid CSP. The content attribute must be set.");
553+
return {
554+
warnings: warnings,
555+
payload: payload
556+
};
557+
}
558+
const directives = Object.fromEntries(content.split(/\s*;\s*/).map((directive)=>{
559+
const [key, ...value] = directive.split(" ");
560+
return [
561+
key,
562+
value.join(" ")
563+
];
564+
}));
565+
payload = payload ?? {};
566+
payload.directives = directives;
567+
if ("report-uri" in directives) warnings.push("The report-uri directive is not supported. Use the Content-Security-Policy-Report-Only HTTP header instead.");
568+
if ("frame-ancestors" in directives) warnings.push("The frame-ancestors directive is not supported. Use the Content-Security-Policy HTTP header instead.");
569+
if ("sandbox" in directives) warnings.push("The sandbox directive is not supported. Use the Content-Security-Policy HTTP header instead.");
546570
return {
547-
warnings: warnings
571+
warnings: warnings,
572+
payload: payload
548573
};
549574
}
550575
function $c322f9a5057eaf5c$var$isInvalidOriginTrial(element) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rviscomi/capo.js",
3-
"version": "1.5.0",
3+
"version": "1.5.1",
44
"description": "Get your ﹤𝚑𝚎𝚊𝚍﹥ in order",
55
"author": "Rick Viscomi",
66
"license": "Apache-2.0",

snippet/capo.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,36 @@ function $580f7ed6bc170ae8$export$6c93e2175c028eeb(element) {
541541
}
542542
function $580f7ed6bc170ae8$var$validateCSP(element) {
543543
const warnings = [];
544-
if (element.matches('meta[http-equiv="Content-Security-Policy-Report-Only" i]')) //https://w3c.github.io/webappsec-csp/#meta-element
545-
warnings.push("CSP Report-Only is forbidden in meta tags");
546-
else if (element.matches('meta[http-equiv="Content-Security-Policy" i]')) warnings.push("meta CSP discouraged. See https://crbug.com/1458493.");
544+
let payload = null;
545+
if (element.matches('meta[http-equiv="Content-Security-Policy-Report-Only" i]')) {
546+
//https://w3c.github.io/webappsec-csp/#meta-element
547+
warnings.push("CSP Report-Only is forbidden in meta tags");
548+
return warnings;
549+
}
550+
if (element.matches('meta[http-equiv="Content-Security-Policy" i]')) warnings.push("meta CSP discouraged. See https://crbug.com/1458493.");
551+
const content = element.getAttribute("content");
552+
if (!content) {
553+
warnings.push("Invalid CSP. The content attribute must be set.");
554+
return {
555+
warnings: warnings,
556+
payload: payload
557+
};
558+
}
559+
const directives = Object.fromEntries(content.split(/\s*;\s*/).map((directive)=>{
560+
const [key, ...value] = directive.split(" ");
561+
return [
562+
key,
563+
value.join(" ")
564+
];
565+
}));
566+
payload = payload ?? {};
567+
payload.directives = directives;
568+
if ("report-uri" in directives) warnings.push("The report-uri directive is not supported. Use the Content-Security-Policy-Report-Only HTTP header instead.");
569+
if ("frame-ancestors" in directives) warnings.push("The frame-ancestors directive is not supported. Use the Content-Security-Policy HTTP header instead.");
570+
if ("sandbox" in directives) warnings.push("The sandbox directive is not supported. Use the Content-Security-Policy HTTP header instead.");
547571
return {
548-
warnings: warnings
572+
warnings: warnings,
573+
payload: payload
549574
};
550575
}
551576
function $580f7ed6bc170ae8$var$isInvalidOriginTrial(element) {

src/lib/validation.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,50 @@ export function getCustomValidations(element) {
185185

186186
function validateCSP(element) {
187187
const warnings = [];
188+
let payload = null;
188189

189190
if (element.matches('meta[http-equiv="Content-Security-Policy-Report-Only" i]')) {
190191
//https://w3c.github.io/webappsec-csp/#meta-element
191192
warnings.push("CSP Report-Only is forbidden in meta tags");
192-
} else if (element.matches('meta[http-equiv="Content-Security-Policy" i]')) {
193+
return warnings;
194+
}
195+
196+
if (element.matches('meta[http-equiv="Content-Security-Policy" i]')) {
193197
warnings.push("meta CSP discouraged. See https://crbug.com/1458493.");
198+
}
199+
200+
const content = element.getAttribute("content");
201+
if (!content) {
202+
warnings.push("Invalid CSP. The content attribute must be set.");
203+
return { warnings, payload };
204+
}
205+
206+
const directives = Object.fromEntries(
207+
content.split(/\s*;\s*/).map((directive) => {
208+
const [key, ...value] = directive.split(" ");
209+
return [key, value.join(" ")];
210+
})
211+
);
212+
payload = payload ?? {};
213+
payload.directives = directives;
194214

195-
// TODO: Validate that CSP doesn't include `report-uri`, `frame-ancestors`, or `sandbox` directives.
215+
if ("report-uri" in directives) {
216+
warnings.push(
217+
"The report-uri directive is not supported. Use the Content-Security-Policy-Report-Only HTTP header instead."
218+
);
219+
}
220+
if ("frame-ancestors" in directives) {
221+
warnings.push(
222+
"The frame-ancestors directive is not supported. Use the Content-Security-Policy HTTP header instead."
223+
);
224+
}
225+
if ("sandbox" in directives) {
226+
warnings.push("The sandbox directive is not supported. Use the Content-Security-Policy HTTP header instead.");
196227
}
197228

198229
return {
199230
warnings,
231+
payload,
200232
};
201233
}
202234

0 commit comments

Comments
 (0)