Closed rhysd closed 3 years ago
@billyvg
BTW, sandbox.require
is sandboxed, but Neovim plugin still can use require()
and module.require()
. For example, it can import fs module by require('fs')
. node-client tries to hide stdin/stdout/stderr from plugins but it'd be still possible to access them by using fs.createReadStream
or something. (please correct me if I'm wrong)
I wonder if this sandbox mechanism is effective. I feel it is difficult to remove all vulnerable APIs without removing require()
. If so, I feel removing the sandbox mechanism would be better keeping plugin architecture simple and avoiding problems from using node's internal APIs.
What do you think?
If so, I feel removing the sandbox mechanism would be better keeping plugin architecture simple and avoiding problems from using node's internal APIs.
yes. Looking at the git logs, I don't see an explanation of why sandbox was introduced. Looks like it's intended to isolate Nvim node.js plugins running in the node-client plugin host? Interesting, but...
sandbox.require
relies on monkey-patching _compile
?@justinmk @billyvg
looks like we are messing with the module cache?
Maybe. But I'm not sure since I don't know how to check the cache was hit or not. And the Module._cache
object is too large to check manually. Outputting it with console.log(Module._cache)
instantly scrolled my terminal over 10000 lines...
As far as I had some trials, this _compile
method is not called while loading plugin via sandbox.require
. Since sandbox.require
relies on monkey-patching this _compile
method, it would not be expected that it is not called.
I don't know why, but the logic to import commonjs modules seemed to be changed. Since sandboxing relies on node's internal (=unstable) APIs, they may be changed even if major version is not changed. I know that the sandboxing worked previously (on Node.js v10 or v12), though.
it's not documented (what do plugin authors need to know/expect?)
Since this project was largely rewritten while I was leaving this project temporarily, I also don't know the reason.
I have created a draft PR #183 on top of this PR, which removes sandboxing. I confirmed all tests passed there after fixing some very flaky unit tests.
@billyvg Sure, thanks for the quick reply. I'll rebase #183.
This PR updates all dependencies to the latest.
I fixed almost all errors, but one error which I could not fix is remaining in integration tests:
This failure said that
process.umask
did not raise an error when an argument is given. Giving an argument toprocess.umask
means trying to change the file creation mode mask. Expected behavior is throwing an exception that it cannot change the mask. node-client imports plugin package in sandbox with wrappingprocess.umask
here. But it seems not working.https://github.com/neovim/node-client/blob/5bfdd65ed4e0ec030567c944c327ad4839eaa30e/packages/example-plugin-decorators/src/index.js#L56
This
umask
function call actually can change the file creation mode mask without throwing any errors.I did some investigation and found that
sandbox.require()
seems not working as intended inneovim/src/host/factory.ts
. Even if a source file is imported viasandbox.require
,process.umask
is still[Function: wrappedUmask]
. It is not monkey-patched correctly. When I callprocess.umask
withvm.runInContext
explicitly, it worked as expected:I'm not understanding the sandboxed-require mechanism, but the implementation is highly depending on undocumented node's module implementation. I guess it was changed recently (for example, ESM was implemented recently in Node.js so commonjs implementation would also be updated).
https://github.com/neovim/node-client/blob/5bfdd65ed4e0ec030567c944c327ad4839eaa30e/packages/neovim/src/host/factory.ts#L62-L87
Close #176