// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node.h" #include "node_internals.h" #include "node_watchdog.h" #include "env.h" #include "env-inl.h" namespace node { using v8::AccessType; using v8::Array; using v8::Boolean; using v8::Context; using v8::External; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::None; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyCallbackInfo; using v8::Script; using v8::String; using v8::TryCatch; using v8::V8; using v8::Value; class ContextifyContext { private: Environment* const env_; Persistent sandbox_; Persistent context_; Persistent proxy_global_; public: explicit ContextifyContext(Environment* env, Local sandbox) : env_(env) , sandbox_(env->isolate(), sandbox) , context_(env->isolate(), CreateV8Context(env)) , proxy_global_(env->isolate(), context()->Global()) { sandbox_.MakeWeak(this, SandboxFreeCallback); sandbox_.MarkIndependent(); } ~ContextifyContext() { context_.Dispose(); proxy_global_.Dispose(); sandbox_.Dispose(); } inline Environment* env() const { return env_; } inline Local context() const { return PersistentToLocal(env()->isolate(), context_); } // This is an object that just keeps an internal pointer to this // ContextifyContext. It's passed to the NamedPropertyHandler. If we // pass the main JavaScript context object we're embedded in, then the // NamedPropertyHandler will store a reference to it forever and keep it // from getting gc'd. Local CreateDataWrapper(Environment* env) { HandleScope scope(node_isolate); Local wrapper = env->script_data_constructor_function()->NewInstance(); NODE_WRAP(wrapper, this); return scope.Close(wrapper); } Local CreateV8Context(Environment* env) { HandleScope scope(node_isolate); Local function_template = FunctionTemplate::New(); function_template->SetHiddenPrototype(true); Local sandbox = PersistentToLocal(node_isolate, sandbox_); function_template->SetClassName(sandbox->GetConstructorName()); Local object_template = function_template->InstanceTemplate(); object_template->SetNamedPropertyHandler(GlobalPropertyGetterCallback, GlobalPropertySetterCallback, GlobalPropertyQueryCallback, GlobalPropertyDeleterCallback, GlobalPropertyEnumeratorCallback, CreateDataWrapper(env)); object_template->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck, GlobalPropertyIndexedAccessCheck); return scope.Close(Context::New(node_isolate, NULL, object_template)); } static void Init(Environment* env, Local target) { Local function_template = FunctionTemplate::New(); function_template->InstanceTemplate()->SetInternalFieldCount(1); env->set_script_data_constructor_function(function_template->GetFunction()); NODE_SET_METHOD(target, "makeContext", MakeContext); NODE_SET_METHOD(target, "isContext", IsContext); } static void MakeContext(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args.GetIsolate()); HandleScope handle_scope(args.GetIsolate()); if (!args[0]->IsObject()) { return ThrowTypeError("sandbox argument must be an object."); } Local sandbox = args[0].As(); Local hidden_name = FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); // Don't allow contextifying a sandbox multiple times. assert(sandbox->GetHiddenValue(hidden_name).IsEmpty()); ContextifyContext* context = new ContextifyContext(env, sandbox); Local hidden_context = External::New(context); sandbox->SetHiddenValue(hidden_name, hidden_context); } static void IsContext(const FunctionCallbackInfo& args) { HandleScope scope(node_isolate); if (!args[0]->IsObject()) { ThrowTypeError("sandbox must be an object"); return; } Local sandbox = args[0].As(); Local hidden_name = FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); args.GetReturnValue().Set(!sandbox->GetHiddenValue(hidden_name).IsEmpty()); } static void SandboxFreeCallback(Isolate* isolate, Persistent* target, ContextifyContext* context) { delete context; } static ContextifyContext* ContextFromContextifiedSandbox( const Local& sandbox) { Local hidden_name = FIXED_ONE_BYTE_STRING(node_isolate, "_contextifyHidden"); Local context_external_v = sandbox->GetHiddenValue(hidden_name); if (context_external_v.IsEmpty() || !context_external_v->IsExternal()) { return NULL; } Local context_external = context_external_v.As(); return static_cast(context_external->Value()); } static bool GlobalPropertyNamedAccessCheck(Local host, Local key, AccessType type, Local data) { return true; } static bool GlobalPropertyIndexedAccessCheck(Local host, uint32_t key, AccessType type, Local data) { return true; } static void GlobalPropertyGetterCallback( Local property, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); ContextifyContext* ctx = NULL; NODE_UNWRAP(args.Data().As(), ContextifyContext, ctx); Local sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); Local rv = sandbox->GetRealNamedProperty(property); if (rv.IsEmpty()) { Local proxy_global = PersistentToLocal(node_isolate, ctx->proxy_global_); rv = proxy_global->GetRealNamedProperty(property); } if (!rv.IsEmpty() && rv == ctx->sandbox_) { rv = PersistentToLocal(node_isolate, ctx->proxy_global_); } args.GetReturnValue().Set(rv); } static void GlobalPropertySetterCallback( Local property, Local value, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); ContextifyContext* ctx = NULL; NODE_UNWRAP(args.Data().As(), ContextifyContext, ctx); PersistentToLocal(node_isolate, ctx->sandbox_)->Set(property, value); } static void GlobalPropertyQueryCallback( Local property, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); ContextifyContext* ctx = NULL; NODE_UNWRAP(args.Data().As(), ContextifyContext, ctx); Local sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); Local proxy_global = PersistentToLocal(node_isolate, ctx->proxy_global_); bool in_sandbox = sandbox->GetRealNamedProperty(property).IsEmpty(); bool in_proxy_global = proxy_global->GetRealNamedProperty(property).IsEmpty(); if (!in_sandbox || !in_proxy_global) { args.GetReturnValue().Set(None); } } static void GlobalPropertyDeleterCallback( Local property, const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); ContextifyContext* ctx = NULL; NODE_UNWRAP(args.Data().As(), ContextifyContext, ctx); bool success = PersistentToLocal(node_isolate, ctx->sandbox_)->Delete(property); if (!success) { success = PersistentToLocal(node_isolate, ctx->proxy_global_)->Delete(property); } args.GetReturnValue().Set(success); } static void GlobalPropertyEnumeratorCallback( const PropertyCallbackInfo& args) { HandleScope scope(node_isolate); ContextifyContext* ctx = NULL; NODE_UNWRAP(args.Data().As(), ContextifyContext, ctx); Local sandbox = PersistentToLocal(node_isolate, ctx->sandbox_); args.GetReturnValue().Set(sandbox->GetPropertyNames()); } }; class ContextifyScript : public ObjectWrap { private: Persistent