Skip to content
This repository was archived by the owner on Aug 31, 2018. It is now read-only.

Commit 86ae8d1

Browse files
committed
worker: implement vm.moveMessagePortToContext()
This should help a lot with actual sandboxing of JS code.
1 parent c927bff commit 86ae8d1

8 files changed

Lines changed: 123 additions & 3 deletions

File tree

lib/vm.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ const {
3030
runInDebugContext
3131
} = process.binding('contextify');
3232

33+
const { moveMessagePortToContext } = process.binding('messaging');
34+
3335
// The binding provides a few useful primitives:
3436
// - Script(code, { filename = "evalmachine.anonymous",
3537
// displayErrors = true } = {})
@@ -143,6 +145,7 @@ module.exports = {
143145
Script,
144146
createContext,
145147
createScript,
148+
moveMessagePortToContext,
146149
runInDebugContext,
147150
runInContext,
148151
runInNewContext,

node.gyp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@
249249
'src/node_http2_core-inl.h',
250250
'src/node_buffer.h',
251251
'src/node_constants.h',
252+
'src/node_contextify.h',
252253
'src/node_debug_options.h',
253254
'src/node_http2.h',
254255
'src/node_http2_state.h',
@@ -680,10 +681,12 @@
680681
'<(OBJ_PATH)<(OBJ_SEPARATOR)handle_wrap.<(OBJ_SUFFIX)',
681682
'<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)',
682683
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)',
684+
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_contextify.<(OBJ_SUFFIX)',
683685
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)',
684686
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_messaging.<(OBJ_SUFFIX)',
685687
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_perf.<(OBJ_SUFFIX)',
686688
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)',
689+
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_watchdog.<(OBJ_SUFFIX)',
687690
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_worker.<(OBJ_SUFFIX)',
688691
'<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)',
689692
'<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)',

src/node.cc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,9 +1426,6 @@ InternalCallbackScope::InternalCallbackScope(Environment* env,
14261426
return;
14271427
}
14281428

1429-
// If you hit this assertion, you forgot to enter the v8::Context first.
1430-
CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
1431-
14321429
if (env->using_domains()) {
14331430
failed_ = DomainEnter(env, object_);
14341431
if (failed_)

src/node_contextify.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,18 @@ void InitContextify(Local<Object> target,
10711071
}
10721072

10731073
} // anonymous namespace
1074+
1075+
MaybeLocal<Context> ContextFromContextifiedSandbox(
1076+
Environment* env,
1077+
Local<Object> sandbox) {
1078+
auto contextify_context =
1079+
ContextifyContext::ContextFromContextifiedSandbox(env, sandbox);
1080+
if (contextify_context == nullptr)
1081+
return MaybeLocal<Context>();
1082+
else
1083+
return contextify_context->context();
1084+
}
1085+
10741086
} // namespace node
10751087

10761088
NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify)

src/node_contextify.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef SRC_NODE_CONTEXTIFY_H_
2+
#define SRC_NODE_CONTEXTIFY_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "v8.h"
7+
8+
namespace node {
9+
10+
class Environment;
11+
12+
v8::MaybeLocal<v8::Context> ContextFromContextifiedSandbox(
13+
Environment* env,
14+
v8::Local<v8::Object> sandbox);
15+
16+
} // namespace node
17+
18+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
19+
20+
21+
#endif // SRC_NODE_CONTEXTIFY_H_

src/node_messaging.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "node_messaging.h"
2+
#include "node_contextify.h"
23
#include "node_internals.h"
34
#include "node_buffer.h"
45
#include "util.h"
@@ -698,6 +699,37 @@ void MessagePort::StartBinding(const FunctionCallbackInfo<Value>& args) {
698699
port->Start();
699700
}
700701

702+
void MessagePort::MoveToContext(const FunctionCallbackInfo<Value>& args) {
703+
Environment* env = Environment::GetCurrent(args);
704+
MessagePort* port;
705+
if (!args[0]->IsObject() ||
706+
(port = Unwrap<MessagePort>(args[0].As<Object>())) == nullptr) {
707+
env->ThrowTypeError("First argument needs to be a MessagePort instance");
708+
}
709+
if (!port->data_) {
710+
env->ThrowError("Cannot transfer a closed MessagePort");
711+
return;
712+
}
713+
if (port->is_privileged_ || port->fm_listener_) {
714+
env->ThrowError("Cannot transfer MessagePort with special semantics");
715+
return;
716+
}
717+
Local<Value> context_arg = args[1];
718+
Local<Context> context;
719+
if (!context_arg->IsObject() ||
720+
!ContextFromContextifiedSandbox(env, context_arg.As<Object>())
721+
.ToLocal(&context)) {
722+
env->ThrowError("Invalid context argument");
723+
return;
724+
}
725+
Context::Scope context_scope(context);
726+
MessagePort* target =
727+
MessagePort::New(env, context, nullptr, std::move(port->data_));
728+
if (target) {
729+
args.GetReturnValue().Set(target->object());
730+
}
731+
}
732+
701733
size_t MessagePort::self_size() const {
702734
Mutex::ScopedLock lock(data_->mutex_);
703735
size_t sz = sizeof(*this) + sizeof(*data_);
@@ -781,6 +813,8 @@ static void InitMessaging(Local<Object> target,
781813
templ->GetFunction(context).ToLocalChecked()).FromJust();
782814
}
783815

816+
env->SetMethod(target, "moveMessagePortToContext",
817+
MessagePort::MoveToContext);
784818
target->Set(context,
785819
env->message_port_constructor_string(),
786820
GetMessagePortConstructor(env, context).ToLocalChecked())

src/node_messaging.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,15 @@ class MessagePort : public HandleWrap {
193193
// Start processing messages on this port as a receiving end.
194194
void Start();
195195

196+
/* constructor */
196197
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
198+
/* prototype methods */
197199
static void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
198200
static void StartBinding(const v8::FunctionCallbackInfo<v8::Value>& args);
199201

202+
/* static */
203+
static void MoveToContext(const v8::FunctionCallbackInfo<v8::Value>& args);
204+
200205
static void Entangle(MessagePort* a, MessagePort* b);
201206
static void Entangle(MessagePort* a, MessagePortData* b);
202207

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint-disable prefer-assert-methods */
2+
'use strict';
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const vm = require('vm');
6+
const { MessageChannel } = require('worker');
7+
8+
{
9+
const context = vm.createContext();
10+
const channel = new MessageChannel();
11+
context.port = vm.moveMessagePortToContext(channel.port1, context);
12+
context.global = context;
13+
const port = channel.port2;
14+
vm.runInContext('(' + function() {
15+
function assert(condition) { if (!condition) throw new Error(); }
16+
17+
{
18+
assert(port instanceof Object);
19+
assert(port.onmessage === undefined);
20+
assert(port.postMessage instanceof Function);
21+
port.onmessage = function(msg) {
22+
assert(msg instanceof Object);
23+
port.postMessage(msg);
24+
};
25+
port.start();
26+
}
27+
28+
{
29+
let threw = false;
30+
try {
31+
port.postMessage(global);
32+
} catch (e) {
33+
assert(e instanceof Object);
34+
assert(e instanceof Error);
35+
threw = true;
36+
}
37+
assert(threw);
38+
}
39+
} + ')()', context);
40+
port.on('message', common.mustCall((msg) => {
41+
assert(msg instanceof Object);
42+
port.close();
43+
}));
44+
port.postMessage({});
45+
}

0 commit comments

Comments
 (0)