Skip to content

Commit 71b18a4

Browse files
committed
inspector: guard protocol value recursion
Signed-off-by: GrinZero <774933704@qq.com>
1 parent a03d5a0 commit 71b18a4

2 files changed

Lines changed: 118 additions & 31 deletions

File tree

‎src/inspector/network_agent.cc‎

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,66 @@
1616
namespace node {
1717
namespace inspector {
1818

19+
using v8::Array;
20+
using v8::Boolean;
21+
using v8::Context;
1922
using v8::HandleScope;
23+
using v8::Int32;
2024
using v8::Isolate;
2125
using v8::Local;
26+
using v8::LocalVector;
27+
using v8::Number;
2228
using v8::Object;
2329
using v8::Uint8Array;
2430
using v8::Value;
2531

2632
constexpr size_t kDefaultMaxTotalBufferSize = 100 * 1024 * 1024; // 100MB
33+
constexpr size_t kMaxProtocolValueDepth = 100;
2734

2835
static void ThrowEventError(v8::Isolate* isolate, const std::string& message) {
2936
isolate->ThrowException(v8::Exception::TypeError(
3037
v8::String::NewFromUtf8(isolate, message.c_str()).ToLocalChecked()));
3138
}
3239

3340
static std::unique_ptr<protocol::Value> V8ToProtocolValue(
34-
Isolate* isolate, v8::Local<v8::Context> context, Local<Value> value) {
41+
Isolate* isolate,
42+
Local<Context> context,
43+
Local<Value> value,
44+
LocalVector<Object>* ancestors) {
3545
if (value->IsNullOrUndefined()) {
3646
return protocol::Value::null();
3747
}
3848
if (value->IsBoolean()) {
39-
return protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
49+
return protocol::FundamentalValue::create(value.As<Boolean>()->Value());
4050
}
4151
if (value->IsInt32()) {
42-
return protocol::FundamentalValue::create(value.As<v8::Int32>()->Value());
52+
return protocol::FundamentalValue::create(value.As<Int32>()->Value());
4353
}
4454
if (value->IsNumber()) {
45-
return protocol::FundamentalValue::create(value.As<v8::Number>()->Value());
55+
return protocol::FundamentalValue::create(value.As<Number>()->Value());
4656
}
4757
if (value->IsString()) {
4858
return protocol::StringValue::create(ToProtocolString(isolate, value));
4959
}
60+
61+
if (!value->IsObject()) {
62+
return nullptr;
63+
}
64+
65+
Local<Object> object = value.As<Object>();
66+
if (ancestors->size() >= kMaxProtocolValueDepth) {
67+
return nullptr;
68+
}
69+
for (const auto& ancestor : *ancestors) {
70+
if (ancestor == object) {
71+
return nullptr;
72+
}
73+
}
74+
ancestors->push_back(object);
75+
auto pop_ancestor = OnScopeLeave([ancestors]() { ancestors->pop_back(); });
76+
5077
if (value->IsArray()) {
51-
Local<v8::Array> array = value.As<v8::Array>();
78+
Local<Array> array = value.As<Array>();
5279
std::unique_ptr<protocol::ListValue> list = protocol::ListValue::create();
5380
list->reserve(array->Length());
5481
for (uint32_t i = 0; i < array->Length(); i++) {
@@ -57,43 +84,46 @@ static std::unique_ptr<protocol::Value> V8ToProtocolValue(
5784
return nullptr;
5885
}
5986
std::unique_ptr<protocol::Value> protocol_value =
60-
V8ToProtocolValue(isolate, context, element);
87+
V8ToProtocolValue(isolate, context, element, ancestors);
6188
if (!protocol_value) {
6289
return nullptr;
6390
}
6491
list->pushValue(std::move(protocol_value));
6592
}
6693
return list;
6794
}
68-
if (value->IsObject()) {
69-
Local<Object> object = value.As<Object>();
70-
Local<v8::Array> property_names;
71-
if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) {
95+
96+
Local<Array> property_names;
97+
if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) {
98+
return nullptr;
99+
}
100+
std::unique_ptr<protocol::DictionaryValue> dict =
101+
protocol::DictionaryValue::create();
102+
for (uint32_t i = 0; i < property_names->Length(); i++) {
103+
Local<Value> property_name;
104+
if (!property_names->Get(context, i).ToLocal(&property_name) ||
105+
!property_name->IsString()) {
72106
return nullptr;
73107
}
74-
std::unique_ptr<protocol::DictionaryValue> dict =
75-
protocol::DictionaryValue::create();
76-
for (uint32_t i = 0; i < property_names->Length(); i++) {
77-
Local<Value> property_name;
78-
if (!property_names->Get(context, i).ToLocal(&property_name) ||
79-
!property_name->IsString()) {
80-
return nullptr;
81-
}
82-
Local<Value> property;
83-
if (!object->Get(context, property_name).ToLocal(&property)) {
84-
return nullptr;
85-
}
86-
std::unique_ptr<protocol::Value> protocol_value =
87-
V8ToProtocolValue(isolate, context, property);
88-
if (!protocol_value) {
89-
return nullptr;
90-
}
91-
dict->setValue(ToProtocolString(isolate, property_name),
92-
std::move(protocol_value));
108+
Local<Value> property;
109+
if (!object->Get(context, property_name).ToLocal(&property)) {
110+
return nullptr;
111+
}
112+
std::unique_ptr<protocol::Value> protocol_value =
113+
V8ToProtocolValue(isolate, context, property, ancestors);
114+
if (!protocol_value) {
115+
return nullptr;
93116
}
94-
return dict;
117+
dict->setValue(ToProtocolString(isolate, property_name),
118+
std::move(protocol_value));
95119
}
96-
return nullptr;
120+
return dict;
121+
}
122+
123+
static std::unique_ptr<protocol::Value> V8ToProtocolValue(
124+
Isolate* isolate, Local<Context> context, Local<Value> value) {
125+
LocalVector<Object> ancestors(isolate);
126+
return V8ToProtocolValue(isolate, context, value, &ancestors);
97127
}
98128

99129
// Create a protocol::Network::Headers from the v8 object.

‎test/parallel/test-inspector-emit-protocol-event-errors.js‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,30 @@ function networkRequest(overrides = {}) {
2929
};
3030
}
3131

32+
const MAX_PROTOCOL_VALUE_DEPTH = 100;
33+
34+
function circularObjectStack() {
35+
const stack = { callFrames: [] };
36+
stack.parent = stack;
37+
return stack;
38+
}
39+
40+
function circularArrayStack() {
41+
const callFrames = [];
42+
callFrames.push(callFrames);
43+
return { callFrames };
44+
}
45+
46+
function deeplyNestedStack(depth) {
47+
const stack = { callFrames: [] };
48+
let current = stack;
49+
for (let i = 0; i < depth; i++) {
50+
current.parent = { callFrames: [] };
51+
current = current.parent;
52+
}
53+
return stack;
54+
}
55+
3256
function networkResponse(overrides = {}) {
3357
return {
3458
requestId: 'response-id',
@@ -257,6 +281,39 @@ const NETWORK_ERROR_CASES = [
257281
}),
258282
'Invalid initiator.stack in event',
259283
],
284+
[
285+
'requestWillBeSent',
286+
networkRequest({
287+
requestId: 'request-id-circular-object-initiator-stack',
288+
initiator: {
289+
type: 'script',
290+
stack: circularObjectStack(),
291+
},
292+
}),
293+
'Invalid initiator.stack in event',
294+
],
295+
[
296+
'requestWillBeSent',
297+
networkRequest({
298+
requestId: 'request-id-circular-array-initiator-stack',
299+
initiator: {
300+
type: 'script',
301+
stack: circularArrayStack(),
302+
},
303+
}),
304+
'Invalid initiator.stack in event',
305+
],
306+
[
307+
'requestWillBeSent',
308+
networkRequest({
309+
requestId: 'request-id-deep-initiator-stack',
310+
initiator: {
311+
type: 'script',
312+
stack: deeplyNestedStack(MAX_PROTOCOL_VALUE_DEPTH + 1),
313+
},
314+
}),
315+
'Invalid initiator.stack in event',
316+
],
260317

261318
[
262319
'responseReceived',

0 commit comments

Comments
 (0)