Skip to content

Commit bbf2adf

Browse files
committed
src: prevent URLPattern property getters from crashing with invalid this
1 parent 2bd5694 commit bbf2adf

File tree

4 files changed

+158
-64
lines changed

4 files changed

+158
-64
lines changed

src/env_properties.h

+1
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@
444444
V(qlogoutputstream_constructor_template, v8::ObjectTemplate) \
445445
V(tcp_constructor_template, v8::FunctionTemplate) \
446446
V(tty_constructor_template, v8::FunctionTemplate) \
447+
V(urlpattern_constructor_template, v8::FunctionTemplate) \
447448
V(write_wrap_template, v8::ObjectTemplate) \
448449
V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) \
449450
V(x509_constructor_template, v8::FunctionTemplate)

src/node_url_pattern.cc

+113-64
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,10 @@ void URLPattern::Test(const FunctionCallbackInfo<Value>& args) {
616616
}
617617

618618
void URLPattern::Protocol(const FunctionCallbackInfo<Value>& info) {
619+
auto env = Environment::GetCurrent(info);
620+
if (!HasInstance(env, info.This())) {
621+
return THROW_ERR_INVALID_THIS(env);
622+
}
619623
URLPattern* url_pattern;
620624
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
621625
Local<Value> result;
@@ -625,6 +629,10 @@ void URLPattern::Protocol(const FunctionCallbackInfo<Value>& info) {
625629
}
626630

627631
void URLPattern::Username(const FunctionCallbackInfo<Value>& info) {
632+
auto env = Environment::GetCurrent(info);
633+
if (!HasInstance(env, info.This())) {
634+
return THROW_ERR_INVALID_THIS(env);
635+
}
628636
URLPattern* url_pattern;
629637
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
630638
Local<Value> result;
@@ -634,6 +642,10 @@ void URLPattern::Username(const FunctionCallbackInfo<Value>& info) {
634642
}
635643

636644
void URLPattern::Password(const FunctionCallbackInfo<Value>& info) {
645+
auto env = Environment::GetCurrent(info);
646+
if (!HasInstance(env, info.This())) {
647+
return THROW_ERR_INVALID_THIS(env);
648+
}
637649
URLPattern* url_pattern;
638650
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
639651
Local<Value> result;
@@ -643,6 +655,10 @@ void URLPattern::Password(const FunctionCallbackInfo<Value>& info) {
643655
}
644656

645657
void URLPattern::Hostname(const FunctionCallbackInfo<Value>& info) {
658+
auto env = Environment::GetCurrent(info);
659+
if (!HasInstance(env, info.This())) {
660+
return THROW_ERR_INVALID_THIS(env);
661+
}
646662
URLPattern* url_pattern;
647663
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
648664
Local<Value> result;
@@ -652,6 +668,10 @@ void URLPattern::Hostname(const FunctionCallbackInfo<Value>& info) {
652668
}
653669

654670
void URLPattern::Port(const FunctionCallbackInfo<Value>& info) {
671+
auto env = Environment::GetCurrent(info);
672+
if (!HasInstance(env, info.This())) {
673+
return THROW_ERR_INVALID_THIS(env);
674+
}
655675
URLPattern* url_pattern;
656676
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
657677
Local<Value> result;
@@ -661,6 +681,10 @@ void URLPattern::Port(const FunctionCallbackInfo<Value>& info) {
661681
}
662682

663683
void URLPattern::Pathname(const FunctionCallbackInfo<Value>& info) {
684+
auto env = Environment::GetCurrent(info);
685+
if (!HasInstance(env, info.This())) {
686+
return THROW_ERR_INVALID_THIS(env);
687+
}
664688
URLPattern* url_pattern;
665689
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
666690
Local<Value> result;
@@ -670,6 +694,10 @@ void URLPattern::Pathname(const FunctionCallbackInfo<Value>& info) {
670694
}
671695

672696
void URLPattern::Search(const FunctionCallbackInfo<Value>& info) {
697+
auto env = Environment::GetCurrent(info);
698+
if (!HasInstance(env, info.This())) {
699+
return THROW_ERR_INVALID_THIS(env);
700+
}
673701
URLPattern* url_pattern;
674702
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
675703
Local<Value> result;
@@ -679,6 +707,10 @@ void URLPattern::Search(const FunctionCallbackInfo<Value>& info) {
679707
}
680708

681709
void URLPattern::Hash(const FunctionCallbackInfo<Value>& info) {
710+
auto env = Environment::GetCurrent(info);
711+
if (!HasInstance(env, info.This())) {
712+
return THROW_ERR_INVALID_THIS(env);
713+
}
682714
URLPattern* url_pattern;
683715
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
684716
Local<Value> result;
@@ -688,6 +720,10 @@ void URLPattern::Hash(const FunctionCallbackInfo<Value>& info) {
688720
}
689721

690722
void URLPattern::HasRegexpGroups(const FunctionCallbackInfo<Value>& info) {
723+
auto env = Environment::GetCurrent(info);
724+
if (!HasInstance(env, info.This())) {
725+
return THROW_ERR_INVALID_THIS(env);
726+
}
691727
URLPattern* url_pattern;
692728
ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This());
693729
info.GetReturnValue().Set(url_pattern->HasRegExpGroups());
@@ -708,75 +744,88 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
708744
registry->Register(URLPattern::Test);
709745
}
710746

747+
Local<FunctionTemplate> URLPattern::GetConstructorTemplate(Environment* env) {
748+
auto tmpl = env->urlpattern_constructor_template();
749+
if (tmpl.IsEmpty()) {
750+
auto attributes = static_cast<PropertyAttribute>(ReadOnly | DontDelete);
751+
tmpl = NewFunctionTemplate(env->isolate(), URLPattern::New);
752+
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "URLPattern"));
753+
auto instance_template = tmpl->InstanceTemplate();
754+
auto prototype_template = tmpl->PrototypeTemplate();
755+
756+
instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount);
757+
prototype_template->SetAccessorProperty(
758+
env->protocol_string(),
759+
FunctionTemplate::New(env->isolate(), URLPattern::Protocol),
760+
Local<FunctionTemplate>(),
761+
attributes);
762+
763+
prototype_template->SetAccessorProperty(
764+
env->username_string(),
765+
FunctionTemplate::New(env->isolate(), URLPattern::Username),
766+
Local<FunctionTemplate>(),
767+
attributes);
768+
769+
prototype_template->SetAccessorProperty(
770+
env->password_string(),
771+
FunctionTemplate::New(env->isolate(), URLPattern::Password),
772+
Local<FunctionTemplate>(),
773+
attributes);
774+
775+
prototype_template->SetAccessorProperty(
776+
env->hostname_string(),
777+
FunctionTemplate::New(env->isolate(), URLPattern::Hostname),
778+
Local<FunctionTemplate>(),
779+
attributes);
780+
781+
prototype_template->SetAccessorProperty(
782+
env->port_string(),
783+
FunctionTemplate::New(env->isolate(), URLPattern::Port),
784+
Local<FunctionTemplate>(),
785+
attributes);
786+
787+
prototype_template->SetAccessorProperty(
788+
env->pathname_string(),
789+
FunctionTemplate::New(env->isolate(), URLPattern::Pathname),
790+
Local<FunctionTemplate>(),
791+
attributes);
792+
793+
prototype_template->SetAccessorProperty(
794+
env->search_string(),
795+
FunctionTemplate::New(env->isolate(), URLPattern::Search),
796+
Local<FunctionTemplate>(),
797+
attributes);
798+
799+
prototype_template->SetAccessorProperty(
800+
env->hash_string(),
801+
FunctionTemplate::New(env->isolate(), URLPattern::Hash),
802+
Local<FunctionTemplate>(),
803+
attributes);
804+
805+
prototype_template->SetAccessorProperty(
806+
env->has_regexp_groups_string(),
807+
FunctionTemplate::New(env->isolate(), URLPattern::HasRegexpGroups),
808+
Local<FunctionTemplate>(),
809+
attributes);
810+
811+
SetProtoMethodNoSideEffect(env->isolate(), tmpl, "exec", URLPattern::Exec);
812+
SetProtoMethodNoSideEffect(env->isolate(), tmpl, "test", URLPattern::Test);
813+
env->set_urlpattern_constructor_template(tmpl);
814+
}
815+
return tmpl;
816+
}
817+
818+
bool URLPattern::HasInstance(Environment* env, Local<Value> value) {
819+
return GetConstructorTemplate(env)->HasInstance(value);
820+
}
821+
711822
static void Initialize(Local<Object> target,
712823
Local<Value> unused,
713824
Local<Context> context,
714825
void* priv) {
715826
Environment* env = Environment::GetCurrent(context);
716-
Isolate* isolate = env->isolate();
717-
auto attributes = static_cast<PropertyAttribute>(ReadOnly | DontDelete);
718-
auto ctor_tmpl = NewFunctionTemplate(isolate, URLPattern::New);
719-
auto instance_template = ctor_tmpl->InstanceTemplate();
720-
auto prototype_template = ctor_tmpl->PrototypeTemplate();
721-
ctor_tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "URLPattern"));
722-
723-
instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount);
724-
prototype_template->SetAccessorProperty(
725-
env->protocol_string(),
726-
FunctionTemplate::New(isolate, URLPattern::Protocol),
727-
Local<FunctionTemplate>(),
728-
attributes);
729-
730-
prototype_template->SetAccessorProperty(
731-
env->username_string(),
732-
FunctionTemplate::New(isolate, URLPattern::Username),
733-
Local<FunctionTemplate>(),
734-
attributes);
735-
736-
prototype_template->SetAccessorProperty(
737-
env->password_string(),
738-
FunctionTemplate::New(isolate, URLPattern::Password),
739-
Local<FunctionTemplate>(),
740-
attributes);
741-
742-
prototype_template->SetAccessorProperty(
743-
env->hostname_string(),
744-
FunctionTemplate::New(isolate, URLPattern::Hostname),
745-
Local<FunctionTemplate>(),
746-
attributes);
747-
748-
prototype_template->SetAccessorProperty(
749-
env->port_string(),
750-
FunctionTemplate::New(isolate, URLPattern::Port),
751-
Local<FunctionTemplate>(),
752-
attributes);
753-
754-
prototype_template->SetAccessorProperty(
755-
env->pathname_string(),
756-
FunctionTemplate::New(isolate, URLPattern::Pathname),
757-
Local<FunctionTemplate>(),
758-
attributes);
759-
760-
prototype_template->SetAccessorProperty(
761-
env->search_string(),
762-
FunctionTemplate::New(isolate, URLPattern::Search),
763-
Local<FunctionTemplate>(),
764-
attributes);
765-
766-
prototype_template->SetAccessorProperty(
767-
env->hash_string(),
768-
FunctionTemplate::New(isolate, URLPattern::Hash),
769-
Local<FunctionTemplate>(),
770-
attributes);
771-
772-
prototype_template->SetAccessorProperty(
773-
env->has_regexp_groups_string(),
774-
FunctionTemplate::New(isolate, URLPattern::HasRegexpGroups),
775-
Local<FunctionTemplate>(),
776-
attributes);
777-
778-
SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "exec", URLPattern::Exec);
779-
SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "test", URLPattern::Test);
827+
auto ctor_tmpl = URLPattern::GetConstructorTemplate(env);
828+
CHECK(!ctor_tmpl.IsEmpty());
780829
SetConstructorFunction(context, target, "URLPattern", ctor_tmpl);
781830
}
782831

src/node_url_pattern.h

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class URLPatternRegexProvider {
3232

3333
class URLPattern : public BaseObject {
3434
public:
35+
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
36+
Environment* env);
37+
static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
38+
3539
URLPattern(Environment* env,
3640
v8::Local<v8::Object> object,
3741
ada::url_pattern<URLPatternRegexProvider>&& url_pattern);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const { URLPattern } = require('url');
6+
const { throws } = require('assert');
7+
8+
const pattern = new URLPattern();
9+
const proto = Object.getPrototypeOf(pattern);
10+
11+
// Verifies that attempts to call the property getters on a URLPattern
12+
// with the incorrect `this` will not crash the process.
13+
[
14+
'protocol',
15+
'username',
16+
'password',
17+
'hostname',
18+
'port',
19+
'pathname',
20+
'search',
21+
'hash',
22+
'hasRegExpGroups',
23+
].forEach((i) => {
24+
const prop = Object.getOwnPropertyDescriptor(proto, i).get;
25+
throws(() => prop({}), {
26+
code: 'ERR_INVALID_THIS',
27+
}, i);
28+
});
29+
30+
// Verifies that attempts to call the exec and test functions
31+
// with the wrong this also throw
32+
33+
const { test, exec } = pattern;
34+
35+
throws(() => test({}), {
36+
message: 'Illegal invocation',
37+
});
38+
throws(() => exec({}), {
39+
message: 'Illegal invocation',
40+
});

0 commit comments

Comments
 (0)