Skip to content

Commit 5b15120

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

File tree

4 files changed

+157
-64
lines changed

4 files changed

+157
-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

+112-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,87 @@ 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+
auto instance_template = tmpl->InstanceTemplate();
753+
auto prototype_template = tmpl->PrototypeTemplate();
754+
755+
instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount);
756+
prototype_template->SetAccessorProperty(
757+
env->protocol_string(),
758+
FunctionTemplate::New(env->isolate(), URLPattern::Protocol),
759+
Local<FunctionTemplate>(),
760+
attributes);
761+
762+
prototype_template->SetAccessorProperty(
763+
env->username_string(),
764+
FunctionTemplate::New(env->isolate(), URLPattern::Username),
765+
Local<FunctionTemplate>(),
766+
attributes);
767+
768+
prototype_template->SetAccessorProperty(
769+
env->password_string(),
770+
FunctionTemplate::New(env->isolate(), URLPattern::Password),
771+
Local<FunctionTemplate>(),
772+
attributes);
773+
774+
prototype_template->SetAccessorProperty(
775+
env->hostname_string(),
776+
FunctionTemplate::New(env->isolate(), URLPattern::Hostname),
777+
Local<FunctionTemplate>(),
778+
attributes);
779+
780+
prototype_template->SetAccessorProperty(
781+
env->port_string(),
782+
FunctionTemplate::New(env->isolate(), URLPattern::Port),
783+
Local<FunctionTemplate>(),
784+
attributes);
785+
786+
prototype_template->SetAccessorProperty(
787+
env->pathname_string(),
788+
FunctionTemplate::New(env->isolate(), URLPattern::Pathname),
789+
Local<FunctionTemplate>(),
790+
attributes);
791+
792+
prototype_template->SetAccessorProperty(
793+
env->search_string(),
794+
FunctionTemplate::New(env->isolate(), URLPattern::Search),
795+
Local<FunctionTemplate>(),
796+
attributes);
797+
798+
prototype_template->SetAccessorProperty(
799+
env->hash_string(),
800+
FunctionTemplate::New(env->isolate(), URLPattern::Hash),
801+
Local<FunctionTemplate>(),
802+
attributes);
803+
804+
prototype_template->SetAccessorProperty(
805+
env->has_regexp_groups_string(),
806+
FunctionTemplate::New(env->isolate(), URLPattern::HasRegexpGroups),
807+
Local<FunctionTemplate>(),
808+
attributes);
809+
810+
SetProtoMethodNoSideEffect(env->isolate(), tmpl, "exec", URLPattern::Exec);
811+
SetProtoMethodNoSideEffect(env->isolate(), tmpl, "test", URLPattern::Test);
812+
env->set_urlpattern_constructor_template(tmpl);
813+
}
814+
return tmpl;
815+
}
816+
817+
bool URLPattern::HasInstance(Environment* env, Local<Value> value) {
818+
return GetConstructorTemplate(env)->HasInstance(value);
819+
}
820+
711821
static void Initialize(Local<Object> target,
712822
Local<Value> unused,
713823
Local<Context> context,
714824
void* priv) {
715825
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);
826+
auto ctor_tmpl = URLPattern::GetConstructorTemplate(env);
827+
CHECK(!ctor_tmpl.IsEmpty());
780828
SetConstructorFunction(context, target, "URLPattern", ctor_tmpl);
781829
}
782830

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)