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

Sanitize the path to the user config before importing #30003

Merged
merged 2 commits into from
Oct 19, 2021
Merged

Sanitize the path to the user config before importing #30003

merged 2 commits into from
Oct 19, 2021

Conversation

ionut-botizan
Copy link
Contributor

@ionut-botizan ionut-botizan commented Oct 17, 2021

Related to #29971

The issue with the dynamic import() is not Windows specific and the current fix is not (entirely) correct, as the import() will still fail in some cases.
The real issue is that import() expects a url-encoded string as parameter and, on Windows, the colon (:) makes it think that it's a fully qualified URL using a protocol that it doesn't recognize. But that's just part of the story...
The other part, which is true for all the POSIX compliant operating systems, is that it will try to decode any url-encoded entities, so, basically, when it encounters a % it will try to interpret the following characters as a hex code. So, for example, if it encounters a file path like /foo%20bar/baz.js, it will interpret it as /foo bar/baz.js, because %20 is the url-encoded <space> character.

The solution for this is to use the url.pathToFileURL() method to properly handle the path, like so:

import { pathToFileURL } from 'url'
// ...
userConfigModule = await import(pathToFileURL(path).href)

This will properly encode the % sign and add the necessary prefix and, given the above example, will generate a url like file:///foo%2520bar/baz.js (where %25 is the url-encoded representation of the % character).


To test this behavior yourself, create the following 2 files:

// ./import%20me.js

module.exports = { "answer": 42 }
// ./main.js

const url = require('url')
const path = require('path')

const absoluteFilePath = path.join(__dirname, 'import%20me.js')
const prefixedFilePath = `file://${absoluteFilePath}`
const escapedFilePath = url.pathToFileURL(absoluteFilePath)

loadFile('ESCAPED', escapedFilePath)
loadFile('PREFIXED', prefixedFilePath)

async function loadFile(method, fullPath) {
	import(fullPath)
		.then(data => console.log(`\nThe ${method} method loaded:\n\t`, data.default))
		.catch(err => console.log(`\nThe ${method} method failed with error:\n\t`, err.message))
}

...and run node main.js.

The output should be something like

The PREFIXED method failed with error:
         Cannot find module 'D:\Playground\path-to-file-url\import me.js' imported from D:\Playground\path-to-file-url\main.js

The ESCAPED method loaded:
         { answer: 42 }

Bug

  • Related issues linked using fixes #number
  • Integration tests added
  • Errors have helpful link attached, see contributing.md

Feature

  • Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
  • Related issues linked using fixes #number
  • Integration tests added
  • Documentation added
  • Telemetry added. In case of a feature if it's used or not.
  • Errors have helpful link attached, see contributing.md

Documentation / Examples

  • Make sure the linting passes

@ijjk
Copy link
Member

ijjk commented Oct 19, 2021

Stats from current PR

Default Build (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
buildDuration 15.6s 15.5s -48ms
buildDurationCached 3.6s 3.5s -44ms
nodeModulesSize 195 MB 195 MB ⚠️ +300 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
/ failed reqs 0 0
/ total time (seconds) 3.633 3.787 ⚠️ +0.15
/ avg req/sec 688.15 660.16 ⚠️ -27.99
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.909 1.899 -0.01
/error-in-render avg req/sec 1309.49 1316.65 +7.16
Client Bundles (main, webpack, commons)
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
558.HASH.js gzip 3.02 kB 3.02 kB
779.HASH.js gzip 179 B 179 B
framework-HASH.js gzip 42.2 kB 42.2 kB
main-HASH.js gzip 25 kB 25 kB
webpack-HASH.js gzip 1.49 kB 1.49 kB
Overall change 71.9 kB 71.9 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
polyfills-a4..dd70.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
_app-HASH.js gzip 977 B 977 B
_error-HASH.js gzip 3.11 kB 3.11 kB
amp-HASH.js gzip 553 B 553 B
css-HASH.js gzip 328 B 328 B
dynamic-HASH.js gzip 2.73 kB 2.73 kB
head-HASH.js gzip 2.37 kB 2.37 kB
hooks-HASH.js gzip 918 B 918 B
image-HASH.js gzip 5.85 kB 5.85 kB
index-HASH.js gzip 260 B 260 B
link-HASH.js gzip 1.66 kB 1.66 kB
routerDirect..HASH.js gzip 320 B 320 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 319 B 319 B
bb14e60e810b..30f.css gzip 125 B 125 B
Overall change 19.9 kB 19.9 kB
Client Build Manifests
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
_buildManifest.js gzip 493 B 493 B
Overall change 493 B 493 B
Rendered Page Sizes
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
index.html gzip 539 B 539 B
link.html gzip 550 B 550 B
withRouter.html gzip 532 B 532 B
Overall change 1.62 kB 1.62 kB

Default Build with SWC (Decrease detected ✓)
General Overall increase ⚠️
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
buildDuration 7.7s 7.6s -32ms
buildDurationCached 3.9s 3.7s -208ms
nodeModulesSize 195 MB 195 MB ⚠️ +300 B
Page Load Tests Overall decrease ⚠️
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
/ failed reqs 0 0
/ total time (seconds) 3.659 3.712 ⚠️ +0.05
/ avg req/sec 683.18 673.46 ⚠️ -9.72
/error-in-render failed reqs 0 0
/error-in-render total time (seconds) 1.842 1.895 ⚠️ +0.05
/error-in-render avg req/sec 1357.05 1319.39 ⚠️ -37.66
Client Bundles (main, webpack, commons)
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
675-HASH.js gzip 13.8 kB 13.8 kB
770.HASH.js gzip 178 B 178 B
framework-HASH.js gzip 50.8 kB 50.8 kB
main-HASH.js gzip 35.2 kB 35.2 kB
webpack-HASH.js gzip 1.64 kB 1.64 kB
Overall change 102 kB 102 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
polyfills-a4..dd70.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
_app-HASH.js gzip 1.33 kB 1.33 kB
_error-HASH.js gzip 180 B 180 B
amp-HASH.js gzip 315 B 315 B
css-HASH.js gzip 331 B 331 B
dynamic-HASH.js gzip 2.79 kB 2.79 kB
head-HASH.js gzip 355 B 355 B
hooks-HASH.js gzip 637 B 637 B
image-HASH.js gzip 555 B 555 B
index-HASH.js gzip 261 B 261 B
link-HASH.js gzip 2.22 kB 2.22 kB
routerDirect..HASH.js gzip 326 B 326 B
script-HASH.js gzip 393 B 393 B
withRouter-HASH.js gzip 322 B 322 B
bb14e60e810b..30f.css gzip 125 B 125 B
Overall change 10.1 kB 10.1 kB
Client Build Manifests
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
_buildManifest.js gzip 511 B 511 B
Overall change 511 B 511 B
Rendered Page Sizes
vercel/next.js canary ionut-botizan/next.js bugfix/user-config-import Change
index.html gzip 539 B 539 B
link.html gzip 552 B 552 B
withRouter.html gzip 533 B 533 B
Overall change 1.62 kB 1.62 kB
Commit: 3526055

Copy link
Member

@ijjk ijjk left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

@ijjk ijjk merged commit d9bd329 into vercel:canary Oct 19, 2021
@vercel vercel locked as resolved and limited conversation to collaborators Jan 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants