X Tutup
The Wayback Machine - https://web.archive.org/web/20211118172755/https://github.com/nodejs/node/pull/35157
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

process: introduce codeGenerationFromString event #35157

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

@vdeturckheim
Copy link
Member

@vdeturckheim vdeturckheim commented Sep 11, 2020

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

This is an alternative to #34863 following a suggestion from @addaleax.

As for #34863, the goal is to provide a way to listen to unsafe code generation from strings as it is not possible to monkeypatch eval.

The event will only be emitted if there is at least one listener on it and removing all listeners on this event will result in the handler in V8 to never be called. In other words, if there is no listener on this event, there should be no performance impact on calling eval or the Function constructor.

doc/api/process.md Outdated Show resolved Hide resolved
Loading
doc/api/process.md Outdated Show resolved Hide resolved
Loading
Loading
lib/internal/bootstrap/node.js Outdated Show resolved Hide resolved
Loading
lib/internal/bootstrap/node.js Outdated Show resolved Hide resolved
Loading
@devsnek
Copy link
Member

@devsnek devsnek commented Sep 11, 2020

This still feels like the wrong abstraction level. What if we did --code-generation-handler ./file.js instead?

Loading

@mmarchini
Copy link
Member

@mmarchini mmarchini commented Sep 11, 2020

A flag won't be usable on many serverless environments, being able to set it at runtime (like this PR proposes) seems more appropriate.

Loading

@devsnek
Copy link
Member

@devsnek devsnek commented Sep 11, 2020

My main concern here is that some random library has no business knowing what i'm passing to eval and new Function. My secondary concern is that the event model is wonkey.

How about this:

const release = v8.setCodeGenerationFromStringHandler((code) => {});
// ...
release();

Once called, it can't be called again until release is called.

Loading

@devsnek devsnek closed this Sep 11, 2020
@devsnek devsnek reopened this Sep 11, 2020
@mmarchini
Copy link
Member

@mmarchini mmarchini commented Sep 12, 2020

This is an overall problem with the events we have on process, even uncaughtException and unhandledRejections have use cases where the application doesn't want modules to attach handlers to it. Kinda outside the scope of this PR, but could we somehow use policies to limit which modules can attach process handlers?

Loading

@guybedford
Copy link
Contributor

@guybedford guybedford commented Sep 12, 2020

Agreed it would be great to see this kind of stuff moved out into loaders or a similar API as a higher level execution context. We previously discussed disabling high-level permissions on process, I would still like to see us deprecate more and more features from the process global over time. Even a new builtin module import for these features could allow policy scoping for who has access to it.

But however it is cut, likely does seem to require a new API away from process and deprecating the old on process.

Loading

@devsnek
Copy link
Member

@devsnek devsnek commented Sep 12, 2020

@mmarchini do you have a constraint that this needs to be a process event? If not, I think we should go with the single-callback api.

Loading

@mmarchini
Copy link
Member

@mmarchini mmarchini commented Sep 12, 2020

No constraints, as long as the handler can be defined during runtime I'm fine with it.

Loading

@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 14, 2020

@devsnek

My main concern here is that some random library has no business knowing what i'm passing to eval and new Function.

That's a very fair point. As of today, almost everything in Node.js can be monkeypatched and leak implementation details. For instance. This is true for core modules, but also for built-in such as RegExp.prototype.exec. eval is the only one I know that is un-mokeypatchable. Most of the time (I'd say 90%), using eval is a bad idea. This helps actually monitor how/why it is used and by who.

I am not sure I understand your secondary concern. This current PR does not change the v8 module.

Loading

@devsnek
Copy link
Member

@devsnek devsnek commented Sep 14, 2020

@vdeturckheim are you saying you want random libraries to be able to tune into all evals and whatnot in the main context?

Loading

@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 14, 2020

@devsnek well right now, any library can spy on most of the things other pieces of code do. That's pretty much how APMs work. eval being the main exception to that rule and being at the same time, often something that should be avoided. Providing runtime visibility on calls made to eval to multiple moniroting tool is pretty useful.

Loading

@devsnek
Copy link
Member

@devsnek devsnek commented Sep 14, 2020

@vdeturckheim I can't tell if you're saying you need the event model or not. can you be a bit more direct? afaict the api I proposed above should work.

Loading

@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 14, 2020

@devsnek gotcha, sorry I missunderstood the question ^^. I only need to place one callback for my use case. But I am afraid there might be multiple libraries needing it (say, one RASP for security, one enterprise compliance tool and an APM), and a single callback would make the use of them exclusive. That's why I used an event emitter here.

Loading

@devsnek
Copy link
Member

@devsnek devsnek commented Sep 14, 2020

I guess in my ideal world I would like to see this:

setCallback((code) => {
  security(code);
  compliance(code);
  apm(code);
})

do people think this is unworkable?

Loading

@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 14, 2020

It would require the end user to set this up and assume no library does it without the end user knowing.
Another solution would be to expose the callback so libraries could composes multiple ones but this would end up witht the same result as the event emitter.

Overall, I found out that in the scope of instrumentation, end users don't really want to know about implementation and vendors often do their own things without considering the other ones really.

Loading

@devsnek
Copy link
Member

@devsnek devsnek commented Sep 15, 2020

Well I won't block on this, but it is certainly not ideal.

Loading

@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 15, 2020

@cjihrig thanks for the review, it should be good :)

Loading

@Trott
Copy link
Member

@Trott Trott commented Sep 16, 2020

Loading

@benjamingr
Copy link
Member

@benjamingr benjamingr commented Sep 16, 2020

I don't understand the use case for this - why would I use this?

Loading

@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 17, 2020

@benjamingr there are multiple reasons why one could want to listen to eval. The one I see and would use is to monitor if some pieces of code use eval and dynamically compare it with inputs (say http request) to ensure there is no exploitable/exploited code injection though this method.

Loading

lib/internal/bootstrap/node.js Outdated Show resolved Hide resolved
Loading
@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Sep 21, 2020

Adding WIP flag as I need to fix a build issue (warning emitted when compiling the callback)

Loading

@vdeturckheim vdeturckheim removed the wip label Sep 22, 2020
The 'codeGenerationFromString' event is emitted
when a call is made to `eval` or `new Function`.
@vdeturckheim vdeturckheim force-pushed the intercept_generated_code_event branch from 321b52b to f1ca100 Sep 22, 2020
@vdeturckheim vdeturckheim marked this pull request as draft Oct 2, 2020
@vdeturckheim
Copy link
Member Author

@vdeturckheim vdeturckheim commented Oct 2, 2020

Converting to draft: the error flow is weird, throwing an error in the event listener makes it ignored unless there is certain calls after eval. I need to dig deeper into that before anything can be merged

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

9 participants
X Tutup