isolated-vm
Advanced tools
+1
-1
| { | ||
| "name": "isolated-vm", | ||
| "version": "1.3.1", | ||
| "version": "1.4.0", | ||
| "description": "Access to multiple isolates", | ||
@@ -5,0 +5,0 @@ "main": "isolated-vm.js", |
+9
-0
@@ -141,2 +141,11 @@ [](https://www.npmjs.com/package/isolated-vm) [](https://github.com/laverdet/isolated-vm/blob/master/LICENSE) [](https://travis-ci.org/laverdet/isolated-vm) | ||
| ##### `isolate.referenceCount` *[number]* | ||
| Returns the total count of active `Reference` instances that belong to this isolate. Note that in | ||
| certain cases many `Reference` instances in JavaScript will point to the same underlying reference | ||
| handle, in which case this number will only reflect the underlying reference handle. This happens | ||
| when you transfer a `Reference` instance via some method which accepts transferable values. This | ||
| will also include underlying reference handles created by isolated-vm like `Script` or `Context` | ||
| objects. | ||
| ### Class: `Context` *[transferable]* | ||
@@ -143,0 +152,0 @@ A context is a sandboxed execution environment within an isolate. Each context contains its own |
@@ -125,2 +125,3 @@ #include "isolate_handle.h" | ||
| "isDisposed", ParameterizeAccessor<decltype(&IsolateHandle::IsDisposedGetter), &IsolateHandle::IsDisposedGetter>(), | ||
| "referenceCount", ParameterizeAccessor<decltype(&IsolateHandle::GetReferenceCount), &IsolateHandle::GetReferenceCount>(), | ||
| "wallTime", ParameterizeAccessor<decltype(&IsolateHandle::GetWallTime), &IsolateHandle::GetWallTime>() | ||
@@ -438,3 +439,3 @@ )); | ||
| */ | ||
| v8::Local<v8::Value> IsolateHandle::GetCpuTime() { | ||
| Local<Value> IsolateHandle::GetCpuTime() { | ||
| auto env = this->isolate->GetIsolate(); | ||
@@ -454,3 +455,3 @@ if (!env) { | ||
| v8::Local<v8::Value> IsolateHandle::GetWallTime() { | ||
| Local<Value> IsolateHandle::GetWallTime() { | ||
| auto env = this->isolate->GetIsolate(); | ||
@@ -471,2 +472,13 @@ if (!env) { | ||
| /** | ||
| * Reference count | ||
| */ | ||
| Local<Value> IsolateHandle::GetReferenceCount() { | ||
| auto env = this->isolate->GetIsolate(); | ||
| if (!env) { | ||
| throw js_generic_error("Isolated is disposed"); | ||
| } | ||
| return Number::New(Isolate::GetCurrent(), env->GetRemotesCount()); | ||
| } | ||
| /** | ||
| * Simple disposal checker | ||
@@ -473,0 +485,0 @@ */ |
@@ -36,2 +36,3 @@ #pragma once | ||
| v8::Local<v8::Value> GetWallTime(); | ||
| v8::Local<v8::Value> GetReferenceCount(); | ||
| v8::Local<v8::Value> IsDisposedGetter(); | ||
@@ -38,0 +39,0 @@ static v8::Local<v8::Value> CreateSnapshot(v8::Local<v8::Array> script_handles, v8::MaybeLocal<v8::String> warmup_handle); |
@@ -126,3 +126,3 @@ #include "environment.h" | ||
| thread_pool_t IsolateEnvironment::Scheduler::thread_pool(std::thread::hardware_concurrency() + 1); | ||
| std::atomic<int> IsolateEnvironment::Scheduler::uv_ref_count(0); | ||
| std::atomic<unsigned int> IsolateEnvironment::Scheduler::uv_ref_count(0); | ||
@@ -129,0 +129,0 @@ IsolateEnvironment::Scheduler::Scheduler() = default; |
@@ -41,2 +41,4 @@ #pragma once | ||
| friend v8::Local<v8::Value> RunWithTimeout(uint32_t timeout_ms, F&& fn); | ||
| template <typename ...Types> | ||
| friend class RemoteTuple; | ||
@@ -194,3 +196,3 @@ public: | ||
| static thread_pool_t thread_pool; | ||
| static std::atomic<int> uv_ref_count; | ||
| static std::atomic<unsigned int> uv_ref_count; | ||
| Status status = Status::Waiting; | ||
@@ -309,2 +311,3 @@ std::mutex mutex; | ||
| bool root; | ||
| std::atomic<unsigned int> remotes_count{0}; | ||
| v8::HeapStatistics last_heap {}; | ||
@@ -462,2 +465,9 @@ std::shared_ptr<BookkeepingStatics> bookkeeping_statics; | ||
| /** | ||
| * Returns the current number of outstanding RemoteHandles<> to this isolate. | ||
| */ | ||
| unsigned int GetRemotesCount() const { | ||
| return remotes_count.load(); | ||
| } | ||
| /** | ||
| * Is this the default nodejs isolate? | ||
@@ -464,0 +474,0 @@ */ |
@@ -43,2 +43,3 @@ #pragma once | ||
| Reset<0, Types...>(); | ||
| IsolateEnvironment::GetCurrent()->remotes_count.fetch_sub(sizeof...(Types)); | ||
| } | ||
@@ -55,2 +56,3 @@ }; | ||
| { | ||
| IsolateEnvironment::GetCurrent()->remotes_count.fetch_add(sizeof...(Types)); | ||
| static_assert(!v8::NonCopyablePersistentTraits<v8::Value>::kResetInDestructor, "Do not reset in destructor"); | ||
@@ -57,0 +59,0 @@ } |
@@ -49,2 +49,4 @@ #pragma once | ||
| struct TimeoutRunner : public Runnable { | ||
| // TODO: This should return a StackStaceHolder instead which would avoid rendering the stack when | ||
| // it is not observed. | ||
| std::string& stack_trace; | ||
@@ -51,0 +53,0 @@ ThreadWait& wait; |
@@ -139,3 +139,3 @@ #include "stack_trace.h" | ||
| std::string StackTraceHolder::RenderSingleStack(v8::Local<v8::StackTrace> stack_trace) { | ||
| std::string StackTraceHolder::RenderSingleStack(Local<StackTrace> stack_trace) { | ||
| Isolate* isolate = Isolate::GetCurrent(); | ||
@@ -146,18 +146,39 @@ std::stringstream ss; | ||
| Local<StackFrame> frame = stack_trace->GetFrame(ii); | ||
| String::Utf8Value script_name(isolate, frame->GetScriptName()); | ||
| int line_number = frame->GetLineNumber(); | ||
| int column = frame->GetColumn(); | ||
| if (frame->IsEval()) { | ||
| if (frame->GetScriptId() == Message::kNoScriptIdInfo) { | ||
| ss <<"\n at [eval]:" <<line_number <<":" <<column; | ||
| } else { | ||
| ss <<"\n at [eval] (" <<*script_name <<":" <<line_number <<":" <<column; | ||
| ss <<"\n at "; | ||
| String::Utf8Value fn_name(isolate, frame->GetFunctionName()); | ||
| if (frame->IsWasm()) { | ||
| String::Utf8Value script_name(isolate, frame->GetScriptName()); | ||
| bool has_name = fn_name.length() != 0 || script_name.length() != 0; | ||
| if (has_name) { | ||
| if (script_name.length() == 0) { | ||
| ss <<*fn_name; | ||
| } else { | ||
| ss <<*script_name; | ||
| if (fn_name.length() != 0) { | ||
| ss <<"." <<*fn_name; | ||
| } | ||
| } | ||
| ss <<" (<WASM>"; | ||
| } | ||
| ss <<frame->GetLineNumber() <<":" <<frame->GetColumn(); | ||
| if (has_name) { | ||
| ss <<")"; | ||
| } | ||
| } else { | ||
| String::Utf8Value fn_name(isolate, frame->GetFunctionName()); | ||
| if (fn_name.length() == 0) { | ||
| ss <<"\n at " <<*script_name <<":" <<line_number <<":" <<column; | ||
| if (frame->IsConstructor()) { | ||
| ss <<"new "; | ||
| if (fn_name.length() == 0) { | ||
| ss <<"<anonymous>"; | ||
| } else { | ||
| ss <<*fn_name; | ||
| } | ||
| } else if (fn_name.length() != 0) { | ||
| ss <<*fn_name; | ||
| } else { | ||
| ss <<"\n at " <<*fn_name <<" (" <<*script_name <<":" <<line_number <<":" <<column <<")"; | ||
| AppendFileLocation(isolate, frame, ss); | ||
| return ss.str(); | ||
| } | ||
| ss <<" ("; | ||
| AppendFileLocation(isolate, frame, ss); | ||
| ss <<")"; | ||
| } | ||
@@ -168,2 +189,23 @@ } | ||
| void StackTraceHolder::AppendFileLocation(Isolate* isolate, Local<StackFrame> frame, std::stringstream& ss) { | ||
| String::Utf8Value script_name(isolate, frame->GetScriptNameOrSourceURL()); | ||
| if (script_name.length() == 0) { | ||
| if (frame->IsEval()) { | ||
| ss <<"[eval]"; | ||
| } else { | ||
| ss <<"<anonymous>"; | ||
| } | ||
| } else { | ||
| ss <<*script_name; | ||
| } | ||
| int line_number = frame->GetLineNumber(); | ||
| if (line_number != -1) { | ||
| ss <<":" <<line_number; | ||
| int column = frame->GetColumn(); | ||
| if (column != -1) { | ||
| ss <<":" <<column; | ||
| } | ||
| } | ||
| } | ||
| } // namespace ivm |
@@ -13,2 +13,4 @@ #pragma once | ||
| class StackTraceHolder : public ClassHandle { | ||
| private: | ||
| static void AppendFileLocation(v8::Isolate* isolate, v8::Local<v8::StackFrame> frame, std::stringstream& ss); | ||
| public: | ||
@@ -15,0 +17,0 @@ v8::Persistent<v8::StackTrace, v8::CopyablePersistentTraits<v8::StackTrace>> stack_trace; |
@@ -85,3 +85,11 @@ #pragma once | ||
| if (maybe_message.ToLocal(&message_handle)) { | ||
| return F(message_handle); | ||
| v8::Local<v8::Object> error = F(message_handle).As<v8::Object>(); | ||
| if (!stack_trace.empty() && isolate->InContext()) { | ||
| v8::MaybeLocal<v8::String> maybe_stack = v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(stack_trace.c_str()), v8::NewStringType::kNormal); | ||
| v8::Local<v8::String> stack; | ||
| if (maybe_stack.ToLocal(&stack)) { | ||
| Unmaybe(error->Set(isolate->GetCurrentContext(), v8_string("stack"), stack)); | ||
| } | ||
| } | ||
| return error; | ||
| } | ||
@@ -88,0 +96,0 @@ // If the MaybeLocal is empty then I think v8 will have an exception on deck. I don't know if |
@@ -279,4 +279,6 @@ #include "reference_handle.h" | ||
| Local<Value> key_inner = key->CopyInto(); | ||
| Local<Object> object = Local<Object>::Cast(ivm::Deref(*reference)); | ||
| // Delete key before transferring in, potentially freeing up some v8 heap | ||
| Unmaybe(object->Delete(context_handle, key_inner)); | ||
| Local<Value> val_inner = val->TransferIn(); | ||
| Local<Object> object = Local<Object>::Cast(ivm::Deref(*reference)); | ||
| did_set = Unmaybe(object->Set(context_handle, key_inner, val_inner)); | ||
@@ -283,0 +285,0 @@ } |
@@ -29,3 +29,3 @@ 'use strict'; | ||
| let i2 = err.stack.indexOf('env2'); | ||
| let i3 = err.stack.indexOf('Module'); | ||
| let i3 = err.stack.indexOf('exception-info.js'); | ||
| if (sync === true) { | ||
@@ -68,1 +68,25 @@ if (i0 == -1 || i1 == -1 || i2 == -1 || i3 == -1 || i0 > i1 || i1 > i2 || i2 > i3) { | ||
| }); | ||
| // Try async recursive | ||
| { | ||
| let isolate = new ivm.Isolate({ memoryLimit: 16}); | ||
| let context = isolate.createContextSync(); | ||
| context.global.setSync('context', context); | ||
| context.global.setSync('isolate', isolate); | ||
| isolate.compileScriptSync('new '+function() { | ||
| let script = isolate.compileScriptSync(` | ||
| function infinite() { | ||
| for(;;); | ||
| } | ||
| infinite(); | ||
| `); | ||
| script.runSync(context, { timeout: 10 }); | ||
| return 'arst'; | ||
| }).run(context).then(_ => console.log('recursive did not throw')).catch(function(err) { | ||
| if (!/infinite/.test(err.stack)) { | ||
| console.log('no recursive stack'); | ||
| } | ||
| }); | ||
| } |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
317428
1.15%1597
1.33%476
1.93%