-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
module: allowed module location list #34379
Conversation
Adds --allowed-module-location option that can be used to limit the paths Node.js is allowed to load modules from. Fixes: nodejs#34124
I think keeping this in policies makes sense, it essentially is a scoping mechanism that doesn't currently exist in the policies configuration. Currently policies must whitelist all permissions for loading files and the escape hatches to allow arbitrary loading are not currently specified to allow arbitrary folders. I also have concerns about symlink escapes if the idea is that you cannot access files outside of a directory we need to write up a design doc on symlinks to write down the reasoning. Likely this can be approached in a few ways if we want to put it in the policy file, but I actually think matching "imports" and "exports" design from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be clear on what guarantees we’re making here.
As far as I can tell, this currently:
- Only affects CommonJS
- Assumes unmodified built-ins and Node.js internals
- Seems like it would give odd results if the current path contains a symbolic link
Generally, I don’t think this is the kind of solution we should be aiming for when it comes to #34124, and I feel strongly that we should remove the Fixes:
tag here. Any kind of opt-in restriction does not solve that issue reasonably, imo.
@@ -64,6 +64,18 @@ If this flag is passed, the behavior can still be set to not abort through | |||
[`process.setUncaughtExceptionCaptureCallback()`][] (and through usage of the | |||
`domain` module that uses it). | |||
|
|||
### `--allowed-module-location` | |||
<!-- YAML | |||
added: REPLACE_ME |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added: REPLACE_ME | |
added: REPLACEME |
@@ -84,13 +84,15 @@ const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); | |||
const manifest = getOptionValue('--experimental-policy') ? | |||
require('internal/process/policy').manifest : | |||
null; | |||
const allowedModuleLocations = getOptionValue('--allowed-module-location'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should cache builtins to avoid mutations from affecting this (comment 1 of n)
const allowedModuleLocations = getOptionValue('--allowed-module-location'); | |
const allowedModuleLocations = getOptionValue('--allowed-module-location'); | |
const { relative: PathRelative, isAbsolute: PathIsAbsolute } = path; |
if (allowedModuleLocations && allowedModuleLocations.length > 0) { | ||
locationAllowed = false; | ||
debug('Allowed module locations count: %d', allowedModuleLocations.length); | ||
for (const allowedLocation of allowedModuleLocations) { | ||
const relative = path.relative(allowedLocation, filename); | ||
debug('Relative path from allowed location "%s" to loaded "%s": %s', | ||
allowedLocation, filename, relative); | ||
if (relative === '' || | ||
(!relative.startsWith('..') && !path.isAbsolute(relative))) { | ||
debug('Module will be allowed'); | ||
locationAllowed = true; | ||
break; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should cache builtins to avoid mutations from affecting this (comment 2 of n)
if (allowedModuleLocations && allowedModuleLocations.length > 0) { | |
locationAllowed = false; | |
debug('Allowed module locations count: %d', allowedModuleLocations.length); | |
for (const allowedLocation of allowedModuleLocations) { | |
const relative = path.relative(allowedLocation, filename); | |
debug('Relative path from allowed location "%s" to loaded "%s": %s', | |
allowedLocation, filename, relative); | |
if (relative === '' || | |
(!relative.startsWith('..') && !path.isAbsolute(relative))) { | |
debug('Module will be allowed'); | |
locationAllowed = true; | |
break; | |
} | |
} | |
} | |
if (allowedModuleLocations && allowedModuleLocations.length > 0) { | |
locationAllowed = false; | |
debug('Allowed module locations count: %d', allowedModuleLocations.length); | |
// avoids mutation of Array.prototype[Symbol.iterator] from affecting this | |
for (let i = 0; i < allowedModuleLocations.length; i++) { | |
const allowedLocation = allowedModuleLocations[i]; | |
const relative = PathRelative(allowedLocation, filename); | |
debug('Relative path from allowed location "%s" to loaded "%s": %s', | |
allowedLocation, filename, relative); | |
if (relative === '' || | |
(!StringPrototypeStartsWith(relative, '..') && !PathIsAbsolute(relative))) { | |
debug('Module will be allowed'); | |
locationAllowed = true; | |
break; | |
} | |
} | |
} |
in order for this approach to be applicable to the ESM loader this PR needs to put a change into:
Per symlinks, I think we could state that preventing symlink traversal isn't an issue unless people use --preserve-symlinks due to realpathing any escape should be on the realpath and be prevented looking at this code. Perhaps @zkochan or @arcanis has seen some model for what to do for symlinks escaping a directory in the real world already? |
I'm not sure that this approach would be an adequate fix. Many tools use variants of the resolution algorithm that Node has no control over - especially because the resulting modules aren't directly required. For example let's take Webpack: even with this PR and the right flag set, nothing would prevent Webpack from bundling code from outside the allowed module tree, since its resolution goes through Imo a better fix (albeit harder to implement) would be to provide tools for Node to prevent all filesystem calls to some parts of the filesystem. |
@arcanis I think fs calls and module loading are fundamentally different due to having different overall affects, e.g. you might want to allow access to a |
I'd also like this to remain scoped to how node performs actions, not how 3rd party tools do things like bundling. Module loading hooks are not fired for webpack's bundling workflows etc since those workflows do not want to actually evaluate the code they are bundling. |
@bmeck the latest version of pnpm uses symlinks that don't escape the |
@bzoz do you plan to keep working on this? |
@aduh95 no, whit the feedback received I don't think this PR is a good aproach. |
OK, closing it for now. |
it seems --allowed-module-location has been removed, right? because i cannot search this flag in the latest nodejs, and i also try to add env variable NODE_ALLOWED_MODULE_LOCATIONS, but it seems not work for this issue, node still search all node_modules for parent folders. |
It was never added in the first place. |
thanks liharb! so how to fix this issue? |
There is no "fix". node will always search upwards for every node_modules dir, at this time. |
See #43828 and related comments.
Policies may serve your needs or that PR if/when it lands.
…On Fri, Aug 19, 2022, 10:02 AM Jordan Harband ***@***.***> wrote:
There is no fix. node will always search upwards for every node_modules
dir.
—
Reply to this email directly, view it on GitHub
<#34379 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABZJIZW5ENJZZYO5AIDM73VZ6OX7ANCNFSM4O2PR3BA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Adds
--allowed-module-location
option that can be used to limit the paths Node.js is allowed to load modules from.Fixes: #34124
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes