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

Remove restriction on '/' prefixes #49449

Open
bmeck opened this issue Aug 5, 2020 · 28 comments
Open

Remove restriction on '/' prefixes #49449

bmeck opened this issue Aug 5, 2020 · 28 comments
Labels
module Issues and PRs related to the module subsystem.

Comments

@bmeck
Copy link
Member

bmeck commented Aug 5, 2020

Given that we have self referential packages, and "imports" in package.json. In addition since // is reserved I think we should remove the reservation since it will by the default URL parser be treated as protocol relative.

@jkrems
Copy link
Contributor

jkrems commented Aug 5, 2020

To be clear, this is about direct import specifiers (https://nodejs.org/api/esm.html#esm_import_specifiers), e.g.:

// imports file:///usr/local/etc/mytool.config.mjs, assuming import.meta.url has file:// protocol.
import config from '/usr/local/etc/mytool.config.mjs';

I think that makes sense. Given "imports", I don't see a strong reason to reserve design space here (IIRC package-relative was the only thing ever discussed for this spot).

@ljharb
Copy link
Member

ljharb commented Aug 5, 2020

Wait, why would we want to allow this? What are the use cases for root-absolute specifiers?

I can't conceive of a time in CJS where requiring from a /-prefixed path wasn't a bug (or bad design, as was later revealed when it was ran on a different machine).

@bmeck
Copy link
Member Author

bmeck commented Aug 5, 2020

@ljharb because it adds consistency and I cannot think of a reason to disallow these specifiers on concrete concerns if we are not reserving them for future features. Any future feature using those prefixes not matching the generally accepted behavior of URL resolution would be bad so it isn't like we can use the feature for other purposes beyond pinning to some kind of root.

@ljharb
Copy link
Member

ljharb commented Aug 5, 2020

I absolutely agree that if / is allowed as a non-// prefix, it would have to function as an absolute path, but I don't think "consistency" is a good argument to enable something that is almost always a footgun.

@bmeck
Copy link
Member Author

bmeck commented Aug 5, 2020

@ljharb why is it a footgun? is there clear reasoning here or is it just a linting issue?

@ljharb
Copy link
Member

ljharb commented Aug 5, 2020

It's a footgun because it's not project-portable or machine-portable; any time you have code that uses /, it's highly likely to break on someone else's machine (including a different docker image, host vs guest, if you migrate from one computer to a new one, etc).

@bmeck
Copy link
Member Author

bmeck commented Aug 5, 2020

@ljharb that statement is true for any given one off script that isn't installed via a package manager and uses bare specifiers for things. I do see utility in various things like the ability to import '/etc/config/foo.json'; regardless.

@jkrems
Copy link
Contributor

jkrems commented Aug 5, 2020

I have definitely imported things using absolute paths. If it's top-level application code that will only ever run in a docker container, I don't see anything wrong with importing something (like a config file) from an absolute path. I haven't seen strong evidence that this behavior caused issues in require but have seen people use it there for - what I would consider - valid reasons. But that's admittedly anecdotal.

@jkrems
Copy link
Contributor

jkrems commented Aug 11, 2020

AFAIK (note: I don't use node as a web server) Node.js doesn't have a directory like this, so we can't really mimic this behavior.

Node.js does have a "directory" like this - because modules in node use the file: protocol and host-relative resolution of / works the same here as it does in HTTP URLs. Resolving /zapp relative to file:///foo/bar is well-defined across platforms. Most importantly it's not generally a path relative to the system root, not even on OSs with POSIX paths. Resolving /a%20b.js will not resolve to the file with POSIX path /a%20b.js. It will resolve to a "completely" different path: /a b.js. Those are two different files.

The fact that Apache may serve some of the http: URLs from some directory on disk is a special case with one particular configuration. E.g. Apache could serve some URLs from a constant string in the config or from running a CGI script or from various directories depending on URL patterns. In terms of resolution, that shouldn't really matter.

@ljharb
Copy link
Member

ljharb commented Aug 11, 2020

That node's implementation uses URLs doesn't mean that node module specifiers are conceptually URLs - they're file paths (which happen to be a subset of URLs).

@bmeck
Copy link
Member Author

bmeck commented Aug 11, 2020

@ljharb the file specifiers are URLs, not paths, they do have fragments taken into consideration.

@ljharb
Copy link
Member

ljharb commented Aug 11, 2020

I realize they are URLs. I'm saying most users will never think of them that way.

@bmeck
Copy link
Member Author

bmeck commented Aug 11, 2020

@ljharb people are using this preservation of URL aspects for things like cache busting or ferrying meta-data already, it isn't something we can decouple.

see docs

@jkrems
Copy link
Contributor

jkrems commented Aug 11, 2020

That node's implementation uses URLs doesn't mean that node module specifiers are conceptually URLs - they're file paths (which happen to be a subset of URLs).

I think it would be dangerous if we allow users to think that they are file paths. If allowing / reinforces that notion, then I'd consider it an argument to keep / "forbidden". Code like import(path.resolve(filename))) may look at first glance like it should work but it will not be portable or safe. It will only work if both filename and the working directory happen to only contain URL-safe characters that are interpreted the same way in both filenames and file: URLs. True for many common filenames but not true for arbitrary filenames.

@ljharb
Copy link
Member

ljharb commented Aug 11, 2020

@DerekNonGeneric that's because MDN is geared towards the web, not node, and on the web, that's a domain-relative URL.

I feel very strongly that if a leading / is allowed, it can only ever mean "the filesystem root", because that's what it already means in CJS.

@jkrems
Copy link
Contributor

jkrems commented Aug 11, 2020

I feel very strongly that if a leading / is allowed, it can only ever mean "the filesystem root", because that's what it already means in CJS.

Yes, for file: referrers, it would roughly be "the file system root". But if we explain it as "just like CommonJS", we're asking for trouble. Because while it may be relative to the file system root, it's not a file system path (!). The following will load two different files, even though the specifiers are equal and both interpreted relative to the file system root:

require('/tmp/a%20b.js'); // the file "/tmp/a%20b.js" ("file:///tmp/a%2520b.js")
import('/tmp/a%20b.js'); // the file "/tmp/a b.js" ("file:///tmp/a%20b.js")

If we don't think people can differentiate between those two different paths, maybe we shouldn't allow a leading slash in import.

P.S.: I think we can expect people to think of imports as URL-based longer term. There's way more JavaScript-first devs than node.js-first devs and hopefully the former will become used to thinking in URLs when using import.

@bmeck
Copy link
Member Author

bmeck commented Aug 12, 2020

@DerekNonGeneric that seems like you just want to run a chroot around node, not that node would actually need to do anything.

@ljharb
Copy link
Member

ljharb commented Aug 18, 2020

Wait, what does / in CJS on Windows map to now??

@ljharb
Copy link
Member

ljharb commented Aug 18, 2020

Right, but "c:" isn't necessarily the fs root on windows - where linux has /, and all volumes are mounted under that, windows has up to 26 (i think) volume roots, and i'm not aware there is any pathable location that contains all of them.

Perhaps it maps to the root of the working directory's drive?

@bmeck
Copy link
Member Author

bmeck commented Aug 18, 2020

@DerekNonGeneric

that's not cross-platform, I don't understand how that is the suggestion here.

It works on everywhere but windows natively and windows has workarounds for this via cygwin, windows subsystem for linux, etc. The point was that the request doesn't seem to be something that node needs to specifically handle, and the details of chroot are quite complex.

@bmeck
Copy link
Member Author

bmeck commented Aug 18, 2020

@DerekNonGeneric there is no point that I stated that windows is second class.

@jkrems
Copy link
Contributor

jkrems commented Aug 18, 2020

After reading the other recent thread, I think my bigger concern is with *nix platforms where people may confuse /some/file/url/path with /some/os/file/name and then run into issues with the different encoding. It seemed very hard to get the point across that there are (some) practical differences between the two because they look so similar.

I would be in favor of a clear warning in our resolver when resolving a host-relative URL against a file: URL, suggesting people use an explicit protocol. Not a really strong opinion (there's plenty of common file naming patterns where it's fine) but it may be worth it.

@GeoffreyBooth GeoffreyBooth transferred this issue from nodejs/modules Sep 1, 2023
@GeoffreyBooth GeoffreyBooth added the module Issues and PRs related to the module subsystem. label Sep 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module Issues and PRs related to the module subsystem.
Projects
None yet
Development

No branches or pull requests

4 participants