Multiple slashes can bypass `null` exports path
privatenumber opened this issue · comments
Version
v16.17.0
Platform
macOS 12.4
Subsystem
No response
Description
Setting null
on an export path can be used to exclude private directories from being exposed (Node.js docs):
// ./node_modules/package/package.json
{
"exports": {
"./*": "./*",
"./internal/*": null
}
}
Although this works to prevent an import like this:
import 'package/internal/file.js'
It can be circumvented by using multiple slashes:
import 'package//internal/file.js'
In UNIX, multiple consecutive slashes are treated as a single slash.
What steps will reproduce the bug?
StackBlitz reproduction
https://stackblitz.com/edit/node-auughm?file=index.js
Reproduction repo link
https://github.com/privatenumber/issue-reproductions/tree/master/reproductions/nodejs/node/44316
Steps
- Create the following files:
node_modules/package/package.json
:
{
"exports": {
"./*": "./*",
"./internal/*": null
}
}
node_modules/package/internal/file.js
:
console.log('Accessed internal file!')
index.mjs
:
import('package/internal/file.js').catch(() => {
console.log('Fail: Single slash import')
})
import('package//internal/file.js').then(() => {
console.log('Success: Double slash import')
})
- Run
node ./index.mjs
How often does it reproduce? Is there a required condition?
Always. No environmental conditions. Just multiple slashes in import paths.
What is the expected behavior?
For import 'package//internal/file.js'
to not resolve.
What do you see instead?
It resolves.
Additional information
No response
I'm able to reproduce on v19.0.0-pre.
Seeing packageResolve()
and packageExportsResolve()
, the packageSubpath
string should begin with any key string of packageConfig.exports
to properly find the bestmatch
and bestMatchSubpath
. It doesn't seem to use a normalized path. Maybe that's intended or a bug.
node/lib/internal/modules/esm/resolve.js
Lines 570 to 578 in a99fa50
/cc @nodejs/loaders @nodejs/modules
It doesn't seem to use a normalized path. Maybe that's intended or a bug.
I think that's intended, IIRC everything related to "exports"
is a URL (or URL pattern), not a path.
That being said, I agree that we should fix this, using one or more slashes should give the same result.