-
Notifications
You must be signed in to change notification settings - Fork 27k
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
Route Loader Trusted Types Violation Fix #34730
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jgoping
requested review from
timneutkens,
ijjk,
shuding and
huozhi
as code owners
February 23, 2022 17:15
jgoping
force-pushed
the
tt-violation-fix-route-loader
branch
from
February 23, 2022 17:16
7c34435
to
b31e0a5
Compare
jgoping
force-pushed
the
tt-violation-fix-route-loader
branch
from
February 25, 2022 15:49
b31e0a5
to
b8fc109
Compare
Stats from current PRDefault Build (Increase detected
|
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
buildDuration | 19.4s | 19.8s | |
buildDurationCached | 7.8s | 7.7s | -52ms |
nodeModulesSize | 367 MB | 368 MB |
Page Load Tests Overall decrease ⚠️
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
/ failed reqs | 0 | 0 | ✓ |
/ total time (seconds) | 3.686 | 3.713 | |
/ avg req/sec | 678.28 | 673.26 | |
/error-in-render failed reqs | 0 | 0 | ✓ |
/error-in-render total time (seconds) | 1.787 | 1.839 | |
/error-in-render avg req/sec | 1399.25 | 1359.59 |
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
450.HASH.js gzip | 179 B | 179 B | ✓ |
framework-HASH.js gzip | 42 kB | 42 kB | ✓ |
main-HASH.js gzip | 27.8 kB | 28 kB | |
webpack-HASH.js gzip | 1.44 kB | 1.44 kB | ✓ |
Overall change | 71.5 kB | 71.7 kB |
Legacy Client Bundles (polyfills)
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 31 kB | 31 kB | ✓ |
Overall change | 31 kB | 31 kB | ✓ |
Client Pages
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
_app-HASH.js gzip | 1.36 kB | 1.36 kB | ✓ |
_error-HASH.js gzip | 194 B | 194 B | ✓ |
amp-HASH.js gzip | 312 B | 312 B | ✓ |
css-HASH.js gzip | 326 B | 326 B | ✓ |
dynamic-HASH.js gzip | 2.57 kB | 2.57 kB | ✓ |
head-HASH.js gzip | 350 B | 350 B | ✓ |
hooks-HASH.js gzip | 919 B | 919 B | ✓ |
image-HASH.js gzip | 5.05 kB | 5.05 kB | ✓ |
index-HASH.js gzip | 263 B | 263 B | ✓ |
link-HASH.js gzip | 2.26 kB | 2.26 kB | ✓ |
routerDirect..HASH.js gzip | 321 B | 321 B | ✓ |
script-HASH.js gzip | 383 B | 383 B | ✓ |
withRouter-HASH.js gzip | 318 B | 318 B | ✓ |
85e02e95b279..7e3.css gzip | 107 B | 107 B | ✓ |
Overall change | 14.7 kB | 14.7 kB | ✓ |
Client Build Manifests
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
_buildManifest.js gzip | 460 B | 460 B | ✓ |
Overall change | 460 B | 460 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
index.html gzip | 530 B | 531 B | |
link.html gzip | 544 B | 544 B | ✓ |
withRouter.html gzip | 526 B | 526 B | ✓ |
Overall change | 1.6 kB | 1.6 kB |
Diffs
Diff for main-HASH.js
@@ -2081,6 +2081,7 @@
var _getAssetPathFromRoute = _interopRequireDefault(
__webpack_require__(3891)
);
+ var _trustedTypes = __webpack_require__(4991);
var _requestIdleCallback = __webpack_require__(9311);
function _interopRequireDefault(obj) {
return obj && obj.__esModule
@@ -2250,6 +2251,7 @@
}
function getFilesForRoute(assetPrefix, route) {
if (false) {
+ var scriptUrl;
}
return getClientBuildManifest().then(function(manifest) {
if (!(route in manifest)) {
@@ -2261,9 +2263,13 @@
return assetPrefix + "/_next/" + encodeURI(entry);
});
return {
- scripts: allFiles.filter(function(v) {
- return v.endsWith(".js");
- }),
+ scripts: allFiles
+ .filter(function(v) {
+ return v.endsWith(".js");
+ })
+ .map(function(v) {
+ return (0, _trustedTypes).__unsafeCreateTrustedScriptURL(v);
+ }),
css: allFiles.filter(function(v) {
return v.endsWith(".css");
})
@@ -2276,7 +2282,7 @@
// disposed and readded. Executing scripts twice has no functional
// differences
if (true) {
- var prom = loadedScripts.get(src);
+ var prom = loadedScripts.get(src.toString());
if (prom) {
return prom;
}
@@ -2284,7 +2290,7 @@
if (document.querySelector('script[src^="'.concat(src, '"]'))) {
return Promise.resolve();
}
- loadedScripts.set(src, (prom = appendScript(src)));
+ loadedScripts.set(src.toString(), (prom = appendScript(src)));
return prom;
} else {
}
@@ -2439,7 +2445,7 @@
return Promise.all(
canPrefetch
? output.scripts.map(function(script) {
- return prefetchViaDom(script, "script");
+ return prefetchViaDom(script.toString(), "script");
})
: []
);
@@ -3215,6 +3221,52 @@
/***/
},
+ /***/ 4991: /***/ function(__unused_webpack_module, exports) {
+ "use strict";
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.__unsafeCreateTrustedScriptURL = __unsafeCreateTrustedScriptURL;
+ /**
+ * Stores the Trusted Types Policy. Starts as undefined and can be set to null
+ * if Trusted Types is not supported in the browser.
+ */ var policy;
+ /**
+ * Getter for the Trusted Types Policy. If it is undefined, it is instantiated
+ * here or set to null if Trusted Types is not supported in the browser.
+ */ function getPolicy() {
+ if (typeof policy === "undefined" && "object" !== "undefined") {
+ var ref;
+ policy =
+ ((ref = window.trustedTypes) === null || ref === void 0
+ ? void 0
+ : ref.createPolicy("nextjs", {
+ createHTML: function(input) {
+ return input;
+ },
+ createScript: function(input) {
+ return input;
+ },
+ createScriptURL: function(input) {
+ return input;
+ }
+ })) || null;
+ }
+ return policy;
+ }
+ function __unsafeCreateTrustedScriptURL(url) {
+ var ref;
+ return (
+ ((ref = getPolicy()) === null || ref === void 0
+ ? void 0
+ : ref.createScriptURL(url)) || url
+ );
+ } //# sourceMappingURL=trusted-types.js.map
+
+ /***/
+ },
+
/***/ 8981: /***/ function(
__unused_webpack_module,
exports,
Diff for index.html
@@ -19,7 +19,7 @@
defer=""
></script>
<script
- src="/_next/static/chunks/main-cc79f475267d4370.js"
+ src="/_next/static/chunks/main-8eb30633721bfb57.js"
defer=""
></script>
<script
Diff for link.html
@@ -19,7 +19,7 @@
defer=""
></script>
<script
- src="/_next/static/chunks/main-cc79f475267d4370.js"
+ src="/_next/static/chunks/main-8eb30633721bfb57.js"
defer=""
></script>
<script
Diff for withRouter.html
@@ -19,7 +19,7 @@
defer=""
></script>
<script
- src="/_next/static/chunks/main-cc79f475267d4370.js"
+ src="/_next/static/chunks/main-8eb30633721bfb57.js"
defer=""
></script>
<script
Default Build with SWC (Increase detected ⚠️ )
General Overall increase ⚠️
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
buildDuration | 24.3s | 24s | -251ms |
buildDurationCached | 7.8s | 7.4s | -422ms |
nodeModulesSize | 367 MB | 368 MB |
Page Load Tests Overall increase ✓
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
/ failed reqs | 0 | 0 | ✓ |
/ total time (seconds) | 3.878 | 3.795 | -0.08 |
/ avg req/sec | 644.59 | 658.69 | +14.1 |
/error-in-render failed reqs | 0 | 0 | ✓ |
/error-in-render total time (seconds) | 1.884 | 1.78 | -0.1 |
/error-in-render avg req/sec | 1326.95 | 1404.27 | +77.32 |
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
450.HASH.js gzip | 179 B | 179 B | ✓ |
framework-HASH.js gzip | 42.1 kB | 42.1 kB | ✓ |
main-HASH.js gzip | 27.8 kB | 28 kB | |
webpack-HASH.js gzip | 1.44 kB | 1.44 kB | ✓ |
Overall change | 71.6 kB | 71.8 kB |
Legacy Client Bundles (polyfills)
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 31 kB | 31 kB | ✓ |
Overall change | 31 kB | 31 kB | ✓ |
Client Pages
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
_app-HASH.js gzip | 1.35 kB | 1.35 kB | ✓ |
_error-HASH.js gzip | 180 B | 180 B | ✓ |
amp-HASH.js gzip | 305 B | 305 B | ✓ |
css-HASH.js gzip | 321 B | 321 B | ✓ |
dynamic-HASH.js gzip | 2.56 kB | 2.56 kB | ✓ |
head-HASH.js gzip | 342 B | 342 B | ✓ |
hooks-HASH.js gzip | 911 B | 911 B | ✓ |
image-HASH.js gzip | 5.08 kB | 5.08 kB | ✓ |
index-HASH.js gzip | 256 B | 256 B | ✓ |
link-HASH.js gzip | 2.28 kB | 2.28 kB | ✓ |
routerDirect..HASH.js gzip | 314 B | 314 B | ✓ |
script-HASH.js gzip | 375 B | 375 B | ✓ |
withRouter-HASH.js gzip | 309 B | 309 B | ✓ |
85e02e95b279..7e3.css gzip | 107 B | 107 B | ✓ |
Overall change | 14.7 kB | 14.7 kB | ✓ |
Client Build Manifests
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
_buildManifest.js gzip | 459 B | 459 B | ✓ |
Overall change | 459 B | 459 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
vercel/next.js canary | jgoping/next.js tt-violation-fix-route-loader | Change | |
---|---|---|---|
index.html gzip | 533 B | 534 B | |
link.html gzip | 547 B | 548 B | |
withRouter.html gzip | 527 B | 528 B | |
Overall change | 1.61 kB | 1.61 kB |
Diffs
Diff for main-HASH.js
@@ -2081,6 +2081,7 @@
var _getAssetPathFromRoute = _interopRequireDefault(
__webpack_require__(3891)
);
+ var _trustedTypes = __webpack_require__(4991);
var _requestIdleCallback = __webpack_require__(9311);
function _interopRequireDefault(obj) {
return obj && obj.__esModule
@@ -2250,6 +2251,7 @@
}
function getFilesForRoute(assetPrefix, route) {
if (false) {
+ var scriptUrl;
}
return getClientBuildManifest().then(function(manifest) {
if (!(route in manifest)) {
@@ -2261,9 +2263,13 @@
return assetPrefix + "/_next/" + encodeURI(entry);
});
return {
- scripts: allFiles.filter(function(v) {
- return v.endsWith(".js");
- }),
+ scripts: allFiles
+ .filter(function(v) {
+ return v.endsWith(".js");
+ })
+ .map(function(v) {
+ return (0, _trustedTypes).__unsafeCreateTrustedScriptURL(v);
+ }),
css: allFiles.filter(function(v) {
return v.endsWith(".css");
})
@@ -2276,7 +2282,7 @@
// disposed and readded. Executing scripts twice has no functional
// differences
if (true) {
- var prom = loadedScripts.get(src);
+ var prom = loadedScripts.get(src.toString());
if (prom) {
return prom;
}
@@ -2284,7 +2290,7 @@
if (document.querySelector('script[src^="'.concat(src, '"]'))) {
return Promise.resolve();
}
- loadedScripts.set(src, (prom = appendScript(src)));
+ loadedScripts.set(src.toString(), (prom = appendScript(src)));
return prom;
} else {
}
@@ -2439,7 +2445,7 @@
return Promise.all(
canPrefetch
? output.scripts.map(function(script) {
- return prefetchViaDom(script, "script");
+ return prefetchViaDom(script.toString(), "script");
})
: []
);
@@ -3215,6 +3221,52 @@
/***/
},
+ /***/ 4991: /***/ function(__unused_webpack_module, exports) {
+ "use strict";
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true
+ });
+ exports.__unsafeCreateTrustedScriptURL = __unsafeCreateTrustedScriptURL;
+ /**
+ * Stores the Trusted Types Policy. Starts as undefined and can be set to null
+ * if Trusted Types is not supported in the browser.
+ */ var policy;
+ /**
+ * Getter for the Trusted Types Policy. If it is undefined, it is instantiated
+ * here or set to null if Trusted Types is not supported in the browser.
+ */ function getPolicy() {
+ if (typeof policy === "undefined" && "object" !== "undefined") {
+ var ref;
+ policy =
+ ((ref = window.trustedTypes) === null || ref === void 0
+ ? void 0
+ : ref.createPolicy("nextjs", {
+ createHTML: function(input) {
+ return input;
+ },
+ createScript: function(input) {
+ return input;
+ },
+ createScriptURL: function(input) {
+ return input;
+ }
+ })) || null;
+ }
+ return policy;
+ }
+ function __unsafeCreateTrustedScriptURL(url) {
+ var ref;
+ return (
+ ((ref = getPolicy()) === null || ref === void 0
+ ? void 0
+ : ref.createScriptURL(url)) || url
+ );
+ } //# sourceMappingURL=trusted-types.js.map
+
+ /***/
+ },
+
/***/ 8981: /***/ function(
__unused_webpack_module,
exports,
Diff for index.html
@@ -19,7 +19,7 @@
defer=""
></script>
<script
- src="/_next/static/chunks/main-cc79f475267d4370.js"
+ src="/_next/static/chunks/main-8eb30633721bfb57.js"
defer=""
></script>
<script
Diff for link.html
@@ -19,7 +19,7 @@
defer=""
></script>
<script
- src="/_next/static/chunks/main-cc79f475267d4370.js"
+ src="/_next/static/chunks/main-8eb30633721bfb57.js"
defer=""
></script>
<script
Diff for withRouter.html
@@ -19,7 +19,7 @@
defer=""
></script>
<script
- src="/_next/static/chunks/main-cc79f475267d4370.js"
+ src="/_next/static/chunks/main-8eb30633721bfb57.js"
defer=""
></script>
<script
@ijjk Hi there! I noticed that you reviewed my previous PR related to Trusted Types. Do you think you could give this PR a review as well? If you have any questions feel free to reach out to me, thank you! |
ijjk
approved these changes
May 3, 2022
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Linked to issue #32209.
Feature
fixes #number
contributing.md
Documentation
There is one tsec violation that is fixed in this PR:
1. ban-script-src-assignment: route-loader.ts
XSS can occur with the line script.src = src in appendScript(src, script) if src can be controlled by a malicious user. From tracing through the code, it was determined that src comes from the function
getFilesForRoute(route)
. The behaviour of this function differs depending on the environment (development vs. production), but in both cases the function will construct strings that lead to valid file paths. These strings depend on two variables:assetPrefix
androute
, but due to the nature of the constructed strings it was determined that the scripts here are safe to use. Thus, the solution was to promote these strings toTrustedScriptURL
s. This is the Trusted Types way of declaring that the script URL passed to the DOM sink is safe from DOM XSS attacks.To create a
TrustedScriptURL
, a policy needs to be created. This policy was put in its own file:client/trusted-types.ts
. This policy has the namenextjs
. If this name should be changed to something else, feel free to change it now. However, once it is released to the public and application developers begin using it, it may be harder to change the value since any application developers with a custom policy name allowlist would now need to update theirnext.config.js
headers to allow this new name.The code was tested in a sample application to ensure it behaved as expected.