diff --git a/src/env_properties.h b/src/env_properties.h index 43725de9b51237..0e7f4c5706dc23 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -444,6 +444,7 @@ V(qlogoutputstream_constructor_template, v8::ObjectTemplate) \ V(tcp_constructor_template, v8::FunctionTemplate) \ V(tty_constructor_template, v8::FunctionTemplate) \ + V(urlpattern_constructor_template, v8::FunctionTemplate) \ V(write_wrap_template, v8::ObjectTemplate) \ V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) \ V(x509_constructor_template, v8::FunctionTemplate) diff --git a/src/node_url_pattern.cc b/src/node_url_pattern.cc index 3d6340565d1e06..a3542d88782afd 100644 --- a/src/node_url_pattern.cc +++ b/src/node_url_pattern.cc @@ -616,6 +616,10 @@ void URLPattern::Test(const FunctionCallbackInfo& args) { } void URLPattern::Protocol(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -625,6 +629,10 @@ void URLPattern::Protocol(const FunctionCallbackInfo& info) { } void URLPattern::Username(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -634,6 +642,10 @@ void URLPattern::Username(const FunctionCallbackInfo& info) { } void URLPattern::Password(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -643,6 +655,10 @@ void URLPattern::Password(const FunctionCallbackInfo& info) { } void URLPattern::Hostname(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -652,6 +668,10 @@ void URLPattern::Hostname(const FunctionCallbackInfo& info) { } void URLPattern::Port(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -661,6 +681,10 @@ void URLPattern::Port(const FunctionCallbackInfo& info) { } void URLPattern::Pathname(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -670,6 +694,10 @@ void URLPattern::Pathname(const FunctionCallbackInfo& info) { } void URLPattern::Search(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -679,6 +707,10 @@ void URLPattern::Search(const FunctionCallbackInfo& info) { } void URLPattern::Hash(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); Local result; @@ -688,6 +720,10 @@ void URLPattern::Hash(const FunctionCallbackInfo& info) { } void URLPattern::HasRegexpGroups(const FunctionCallbackInfo& info) { + auto env = Environment::GetCurrent(info); + if (!HasInstance(env, info.This())) { + return THROW_ERR_INVALID_THIS(env); + } URLPattern* url_pattern; ASSIGN_OR_RETURN_UNWRAP(&url_pattern, info.This()); info.GetReturnValue().Set(url_pattern->HasRegExpGroups()); @@ -708,75 +744,88 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(URLPattern::Test); } +Local URLPattern::GetConstructorTemplate(Environment* env) { + auto tmpl = env->urlpattern_constructor_template(); + if (tmpl.IsEmpty()) { + auto attributes = static_cast(ReadOnly | DontDelete); + tmpl = NewFunctionTemplate(env->isolate(), URLPattern::New); + tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "URLPattern")); + auto instance_template = tmpl->InstanceTemplate(); + auto prototype_template = tmpl->PrototypeTemplate(); + + instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount); + prototype_template->SetAccessorProperty( + env->protocol_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Protocol), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->username_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Username), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->password_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Password), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->hostname_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Hostname), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->port_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Port), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->pathname_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Pathname), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->search_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Search), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->hash_string(), + FunctionTemplate::New(env->isolate(), URLPattern::Hash), + Local(), + attributes); + + prototype_template->SetAccessorProperty( + env->has_regexp_groups_string(), + FunctionTemplate::New(env->isolate(), URLPattern::HasRegexpGroups), + Local(), + attributes); + + SetProtoMethodNoSideEffect(env->isolate(), tmpl, "exec", URLPattern::Exec); + SetProtoMethodNoSideEffect(env->isolate(), tmpl, "test", URLPattern::Test); + env->set_urlpattern_constructor_template(tmpl); + } + return tmpl; +} + +bool URLPattern::HasInstance(Environment* env, Local value) { + return GetConstructorTemplate(env)->HasInstance(value); +} + static void Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); - Isolate* isolate = env->isolate(); - auto attributes = static_cast(ReadOnly | DontDelete); - auto ctor_tmpl = NewFunctionTemplate(isolate, URLPattern::New); - auto instance_template = ctor_tmpl->InstanceTemplate(); - auto prototype_template = ctor_tmpl->PrototypeTemplate(); - ctor_tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "URLPattern")); - - instance_template->SetInternalFieldCount(URLPattern::kInternalFieldCount); - prototype_template->SetAccessorProperty( - env->protocol_string(), - FunctionTemplate::New(isolate, URLPattern::Protocol), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->username_string(), - FunctionTemplate::New(isolate, URLPattern::Username), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->password_string(), - FunctionTemplate::New(isolate, URLPattern::Password), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->hostname_string(), - FunctionTemplate::New(isolate, URLPattern::Hostname), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->port_string(), - FunctionTemplate::New(isolate, URLPattern::Port), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->pathname_string(), - FunctionTemplate::New(isolate, URLPattern::Pathname), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->search_string(), - FunctionTemplate::New(isolate, URLPattern::Search), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->hash_string(), - FunctionTemplate::New(isolate, URLPattern::Hash), - Local(), - attributes); - - prototype_template->SetAccessorProperty( - env->has_regexp_groups_string(), - FunctionTemplate::New(isolate, URLPattern::HasRegexpGroups), - Local(), - attributes); - - SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "exec", URLPattern::Exec); - SetProtoMethodNoSideEffect(isolate, ctor_tmpl, "test", URLPattern::Test); + auto ctor_tmpl = URLPattern::GetConstructorTemplate(env); + CHECK(!ctor_tmpl.IsEmpty()); SetConstructorFunction(context, target, "URLPattern", ctor_tmpl); } diff --git a/src/node_url_pattern.h b/src/node_url_pattern.h index 99a2521513b55a..be6f30e96daa5b 100644 --- a/src/node_url_pattern.h +++ b/src/node_url_pattern.h @@ -32,6 +32,10 @@ class URLPatternRegexProvider { class URLPattern : public BaseObject { public: + static v8::Local GetConstructorTemplate( + Environment* env); + static bool HasInstance(Environment* env, v8::Local value); + URLPattern(Environment* env, v8::Local object, ada::url_pattern&& url_pattern); diff --git a/test/parallel/test-urlpattern-invalidthis.js b/test/parallel/test-urlpattern-invalidthis.js new file mode 100644 index 00000000000000..254baa40c91fd9 --- /dev/null +++ b/test/parallel/test-urlpattern-invalidthis.js @@ -0,0 +1,40 @@ +'use strict'; + +require('../common'); + +const { URLPattern } = require('url'); +const { throws } = require('assert'); + +const pattern = new URLPattern(); +const proto = Object.getPrototypeOf(pattern); + +// Verifies that attempts to call the property getters on a URLPattern +// with the incorrect `this` will not crash the process. +[ + 'protocol', + 'username', + 'password', + 'hostname', + 'port', + 'pathname', + 'search', + 'hash', + 'hasRegExpGroups', +].forEach((i) => { + const prop = Object.getOwnPropertyDescriptor(proto, i).get; + throws(() => prop({}), { + code: 'ERR_INVALID_THIS', + }, i); +}); + +// Verifies that attempts to call the exec and test functions +// with the wrong this also throw + +const { test, exec } = pattern; + +throws(() => test({}), { + message: 'Illegal invocation', +}); +throws(() => exec({}), { + message: 'Illegal invocation', +});