• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "debug_utils-inl.h"
2 #include "env-inl.h"
3 #include "node_errors.h"
4 #include "node_external_reference.h"
5 #include "node_i18n.h"
6 #include "node_process-inl.h"
7 
8 #include <time.h>  // tzset(), _tzset()
9 
10 namespace node {
11 using v8::Array;
12 using v8::Boolean;
13 using v8::Context;
14 using v8::DontDelete;
15 using v8::DontEnum;
16 using v8::FunctionTemplate;
17 using v8::HandleScope;
18 using v8::Integer;
19 using v8::Isolate;
20 using v8::Just;
21 using v8::Local;
22 using v8::Maybe;
23 using v8::MaybeLocal;
24 using v8::Name;
25 using v8::NamedPropertyHandlerConfiguration;
26 using v8::NewStringType;
27 using v8::Nothing;
28 using v8::Object;
29 using v8::ObjectTemplate;
30 using v8::PropertyCallbackInfo;
31 using v8::PropertyDescriptor;
32 using v8::PropertyHandlerFlags;
33 using v8::ReadOnly;
34 using v8::String;
35 using v8::Value;
36 
37 class RealEnvStore final : public KVStore {
38  public:
39   MaybeLocal<String> Get(Isolate* isolate, Local<String> key) const override;
40   Maybe<std::string> Get(const char* key) const override;
41   void Set(Isolate* isolate, Local<String> key, Local<String> value) override;
42   int32_t Query(Isolate* isolate, Local<String> key) const override;
43   int32_t Query(const char* key) const override;
44   void Delete(Isolate* isolate, Local<String> key) override;
45   Local<Array> Enumerate(Isolate* isolate) const override;
46 };
47 
48 class MapKVStore final : public KVStore {
49  public:
50   MaybeLocal<String> Get(Isolate* isolate, Local<String> key) const override;
51   Maybe<std::string> Get(const char* key) const override;
52   void Set(Isolate* isolate, Local<String> key, Local<String> value) override;
53   int32_t Query(Isolate* isolate, Local<String> key) const override;
54   int32_t Query(const char* key) const override;
55   void Delete(Isolate* isolate, Local<String> key) override;
56   Local<Array> Enumerate(Isolate* isolate) const override;
57 
58   std::shared_ptr<KVStore> Clone(Isolate* isolate) const override;
59 
60   MapKVStore() = default;
MapKVStore(const MapKVStore & other)61   MapKVStore(const MapKVStore& other) : KVStore(), map_(other.map_) {}
62 
63  private:
64   mutable Mutex mutex_;
65   std::unordered_map<std::string, std::string> map_;
66 };
67 
68 namespace per_process {
69 Mutex env_var_mutex;
70 std::shared_ptr<KVStore> system_environment = std::make_shared<RealEnvStore>();
71 }  // namespace per_process
72 
73 template <typename T>
DateTimeConfigurationChangeNotification(Isolate * isolate,const T & key,const char * val=nullptr)74 void DateTimeConfigurationChangeNotification(
75     Isolate* isolate,
76     const T& key,
77     const char* val = nullptr) {
78   if (key.length() == 2 && key[0] == 'T' && key[1] == 'Z') {
79 #ifdef __POSIX__
80     tzset();
81     isolate->DateTimeConfigurationChangeNotification(
82         Isolate::TimeZoneDetection::kRedetect);
83 #else
84     _tzset();
85 
86 # if defined(NODE_HAVE_I18N_SUPPORT)
87     isolate->DateTimeConfigurationChangeNotification(
88         Isolate::TimeZoneDetection::kSkip);
89 
90     // On windows, the TZ environment is not supported out of the box.
91     // By default, v8 will only be able to detect the system configured
92     // timezone. This supports using the TZ environment variable to set
93     // the default timezone instead.
94     if (val != nullptr) i18n::SetDefaultTimeZone(val);
95 # else
96     isolate->DateTimeConfigurationChangeNotification(
97         Isolate::TimeZoneDetection::kRedetect);
98 # endif
99 #endif
100   }
101 }
102 
Get(const char * key) const103 Maybe<std::string> RealEnvStore::Get(const char* key) const {
104   Mutex::ScopedLock lock(per_process::env_var_mutex);
105 
106   size_t init_sz = 256;
107   MaybeStackBuffer<char, 256> val;
108   int ret = uv_os_getenv(key, *val, &init_sz);
109 
110   if (ret == UV_ENOBUFS) {
111     // Buffer is not large enough, reallocate to the updated init_sz
112     // and fetch env value again.
113     val.AllocateSufficientStorage(init_sz);
114     ret = uv_os_getenv(key, *val, &init_sz);
115   }
116 
117   if (ret >= 0) {  // Env key value fetch success.
118     return Just(std::string(*val, init_sz));
119   }
120 
121   return Nothing<std::string>();
122 }
123 
Get(Isolate * isolate,Local<String> property) const124 MaybeLocal<String> RealEnvStore::Get(Isolate* isolate,
125                                      Local<String> property) const {
126   node::Utf8Value key(isolate, property);
127   Maybe<std::string> value = Get(*key);
128 
129   if (value.IsJust()) {
130     std::string val = value.FromJust();
131     return String::NewFromUtf8(
132         isolate, val.data(), NewStringType::kNormal, val.size());
133   }
134 
135   return MaybeLocal<String>();
136 }
137 
Set(Isolate * isolate,Local<String> property,Local<String> value)138 void RealEnvStore::Set(Isolate* isolate,
139                        Local<String> property,
140                        Local<String> value) {
141   Mutex::ScopedLock lock(per_process::env_var_mutex);
142 
143   node::Utf8Value key(isolate, property);
144   node::Utf8Value val(isolate, value);
145 
146 #ifdef _WIN32
147   if (key.length() > 0 && key[0] == '=') return;
148 #endif
149   uv_os_setenv(*key, *val);
150   DateTimeConfigurationChangeNotification(isolate, key, *val);
151 }
152 
Query(const char * key) const153 int32_t RealEnvStore::Query(const char* key) const {
154   Mutex::ScopedLock lock(per_process::env_var_mutex);
155 
156   char val[2];
157   size_t init_sz = sizeof(val);
158   int ret = uv_os_getenv(key, val, &init_sz);
159 
160   if (ret == UV_ENOENT) {
161     return -1;
162   }
163 
164 #ifdef _WIN32
165   if (key[0] == '=') {
166     return static_cast<int32_t>(ReadOnly) |
167            static_cast<int32_t>(DontDelete) |
168            static_cast<int32_t>(DontEnum);
169   }
170 #endif
171 
172   return 0;
173 }
174 
Query(Isolate * isolate,Local<String> property) const175 int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
176   node::Utf8Value key(isolate, property);
177   return Query(*key);
178 }
179 
Delete(Isolate * isolate,Local<String> property)180 void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
181   Mutex::ScopedLock lock(per_process::env_var_mutex);
182 
183   node::Utf8Value key(isolate, property);
184   uv_os_unsetenv(*key);
185   DateTimeConfigurationChangeNotification(isolate, key);
186 }
187 
Enumerate(Isolate * isolate) const188 Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {
189   Mutex::ScopedLock lock(per_process::env_var_mutex);
190   uv_env_item_t* items;
191   int count;
192 
193   auto cleanup = OnScopeLeave([&]() { uv_os_free_environ(items, count); });
194   CHECK_EQ(uv_os_environ(&items, &count), 0);
195 
196   MaybeStackBuffer<Local<Value>, 256> env_v(count);
197   int env_v_index = 0;
198   for (int i = 0; i < count; i++) {
199 #ifdef _WIN32
200     // If the key starts with '=' it is a hidden environment variable.
201     if (items[i].name[0] == '=') continue;
202 #endif
203     MaybeLocal<String> str = String::NewFromUtf8(isolate, items[i].name);
204     if (str.IsEmpty()) {
205       isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
206       return Local<Array>();
207     }
208     env_v[env_v_index++] = str.ToLocalChecked();
209   }
210 
211   return Array::New(isolate, env_v.out(), env_v_index);
212 }
213 
Clone(Isolate * isolate) const214 std::shared_ptr<KVStore> KVStore::Clone(Isolate* isolate) const {
215   HandleScope handle_scope(isolate);
216   Local<Context> context = isolate->GetCurrentContext();
217 
218   std::shared_ptr<KVStore> copy = KVStore::CreateMapKVStore();
219   Local<Array> keys = Enumerate(isolate);
220   uint32_t keys_length = keys->Length();
221   for (uint32_t i = 0; i < keys_length; i++) {
222     Local<Value> key = keys->Get(context, i).ToLocalChecked();
223     CHECK(key->IsString());
224     copy->Set(isolate,
225               key.As<String>(),
226               Get(isolate, key.As<String>()).ToLocalChecked());
227   }
228   return copy;
229 }
230 
Get(const char * key) const231 Maybe<std::string> MapKVStore::Get(const char* key) const {
232   Mutex::ScopedLock lock(mutex_);
233   auto it = map_.find(key);
234   return it == map_.end() ? Nothing<std::string>() : Just(it->second);
235 }
236 
Get(Isolate * isolate,Local<String> key) const237 MaybeLocal<String> MapKVStore::Get(Isolate* isolate, Local<String> key) const {
238   Utf8Value str(isolate, key);
239   Maybe<std::string> value = Get(*str);
240   if (value.IsNothing()) return Local<String>();
241   std::string val = value.FromJust();
242   return String::NewFromUtf8(
243       isolate, val.data(), NewStringType::kNormal, val.size());
244 }
245 
Set(Isolate * isolate,Local<String> key,Local<String> value)246 void MapKVStore::Set(Isolate* isolate, Local<String> key, Local<String> value) {
247   Mutex::ScopedLock lock(mutex_);
248   Utf8Value key_str(isolate, key);
249   Utf8Value value_str(isolate, value);
250   if (*key_str != nullptr && key_str.length() > 0 && *value_str != nullptr) {
251     map_[std::string(*key_str, key_str.length())] =
252         std::string(*value_str, value_str.length());
253   }
254 }
255 
Query(const char * key) const256 int32_t MapKVStore::Query(const char* key) const {
257   Mutex::ScopedLock lock(mutex_);
258   return map_.find(key) == map_.end() ? -1 : 0;
259 }
260 
Query(Isolate * isolate,Local<String> key) const261 int32_t MapKVStore::Query(Isolate* isolate, Local<String> key) const {
262   Utf8Value str(isolate, key);
263   return Query(*str);
264 }
265 
Delete(Isolate * isolate,Local<String> key)266 void MapKVStore::Delete(Isolate* isolate, Local<String> key) {
267   Mutex::ScopedLock lock(mutex_);
268   Utf8Value str(isolate, key);
269   map_.erase(std::string(*str, str.length()));
270 }
271 
Enumerate(Isolate * isolate) const272 Local<Array> MapKVStore::Enumerate(Isolate* isolate) const {
273   Mutex::ScopedLock lock(mutex_);
274   std::vector<Local<Value>> values;
275   values.reserve(map_.size());
276   for (const auto& pair : map_) {
277     values.emplace_back(
278         String::NewFromUtf8(isolate, pair.first.data(),
279                             NewStringType::kNormal, pair.first.size())
280             .ToLocalChecked());
281   }
282   return Array::New(isolate, values.data(), values.size());
283 }
284 
Clone(Isolate * isolate) const285 std::shared_ptr<KVStore> MapKVStore::Clone(Isolate* isolate) const {
286   return std::make_shared<MapKVStore>(*this);
287 }
288 
CreateMapKVStore()289 std::shared_ptr<KVStore> KVStore::CreateMapKVStore() {
290   return std::make_shared<MapKVStore>();
291 }
292 
AssignFromObject(Local<Context> context,Local<Object> entries)293 Maybe<bool> KVStore::AssignFromObject(Local<Context> context,
294                                       Local<Object> entries) {
295   Isolate* isolate = context->GetIsolate();
296   HandleScope handle_scope(isolate);
297   Local<Array> keys;
298   if (!entries->GetOwnPropertyNames(context).ToLocal(&keys))
299     return Nothing<bool>();
300   uint32_t keys_length = keys->Length();
301   for (uint32_t i = 0; i < keys_length; i++) {
302     Local<Value> key;
303     if (!keys->Get(context, i).ToLocal(&key))
304       return Nothing<bool>();
305     if (!key->IsString()) continue;
306 
307     Local<Value> value;
308     Local<String> value_string;
309     if (!entries->Get(context, key).ToLocal(&value) ||
310         !value->ToString(context).ToLocal(&value_string)) {
311       return Nothing<bool>();
312     }
313 
314     Set(isolate, key.As<String>(), value_string);
315   }
316   return Just(true);
317 }
318 
319 // TODO(bnoordhuis) Not super efficient but called infrequently. Not worth
320 // the trouble yet of specializing for RealEnvStore and MapKVStore.
AssignToObject(v8::Isolate * isolate,v8::Local<v8::Context> context,v8::Local<v8::Object> object)321 Maybe<bool> KVStore::AssignToObject(v8::Isolate* isolate,
322                                     v8::Local<v8::Context> context,
323                                     v8::Local<v8::Object> object) {
324   HandleScope scope(isolate);
325   Local<Array> keys = Enumerate(isolate);
326   uint32_t keys_length = keys->Length();
327   for (uint32_t i = 0; i < keys_length; i++) {
328     Local<Value> key;
329     Local<String> value;
330     bool ok = keys->Get(context, i).ToLocal(&key);
331     ok = ok && key->IsString();
332     ok = ok && Get(isolate, key.As<String>()).ToLocal(&value);
333     ok = ok && object->Set(context, key, value).To(&ok);
334     if (!ok) return Nothing<bool>();
335   }
336   return Just(true);
337 }
338 
EnvGetter(Local<Name> property,const PropertyCallbackInfo<Value> & info)339 static void EnvGetter(Local<Name> property,
340                       const PropertyCallbackInfo<Value>& info) {
341   Environment* env = Environment::GetCurrent(info);
342   CHECK(env->has_run_bootstrapping_code());
343   if (property->IsSymbol()) {
344     return info.GetReturnValue().SetUndefined();
345   }
346   CHECK(property->IsString());
347   MaybeLocal<String> value_string =
348       env->env_vars()->Get(env->isolate(), property.As<String>());
349   if (!value_string.IsEmpty()) {
350     info.GetReturnValue().Set(value_string.ToLocalChecked());
351   }
352 }
353 
EnvSetter(Local<Name> property,Local<Value> value,const PropertyCallbackInfo<Value> & info)354 static void EnvSetter(Local<Name> property,
355                       Local<Value> value,
356                       const PropertyCallbackInfo<Value>& info) {
357   Environment* env = Environment::GetCurrent(info);
358   CHECK(env->has_run_bootstrapping_code());
359   // calling env->EmitProcessEnvWarning() sets a variable indicating that
360   // warnings have been emitted. It should be called last after other
361   // conditions leading to a warning have been met.
362   if (env->options()->pending_deprecation && !value->IsString() &&
363       !value->IsNumber() && !value->IsBoolean() &&
364       env->EmitProcessEnvWarning()) {
365     if (ProcessEmitDeprecationWarning(
366             env,
367             "Assigning any value other than a string, number, or boolean to a "
368             "process.env property is deprecated. Please make sure to convert "
369             "the "
370             "value to a string before setting process.env with it.",
371             "DEP0104")
372             .IsNothing())
373       return;
374   }
375 
376   Local<String> key;
377   Local<String> value_string;
378   if (!property->ToString(env->context()).ToLocal(&key) ||
379       !value->ToString(env->context()).ToLocal(&value_string)) {
380     return;
381   }
382 
383   env->env_vars()->Set(env->isolate(), key, value_string);
384 
385   // Whether it worked or not, always return value.
386   info.GetReturnValue().Set(value);
387 }
388 
EnvQuery(Local<Name> property,const PropertyCallbackInfo<Integer> & info)389 static void EnvQuery(Local<Name> property,
390                      const PropertyCallbackInfo<Integer>& info) {
391   Environment* env = Environment::GetCurrent(info);
392   CHECK(env->has_run_bootstrapping_code());
393   if (property->IsString()) {
394     int32_t rc = env->env_vars()->Query(env->isolate(), property.As<String>());
395     if (rc != -1) info.GetReturnValue().Set(rc);
396   }
397 }
398 
EnvDeleter(Local<Name> property,const PropertyCallbackInfo<Boolean> & info)399 static void EnvDeleter(Local<Name> property,
400                        const PropertyCallbackInfo<Boolean>& info) {
401   Environment* env = Environment::GetCurrent(info);
402   CHECK(env->has_run_bootstrapping_code());
403   if (property->IsString()) {
404     env->env_vars()->Delete(env->isolate(), property.As<String>());
405   }
406 
407   // process.env never has non-configurable properties, so always
408   // return true like the tc39 delete operator.
409   info.GetReturnValue().Set(true);
410 }
411 
EnvEnumerator(const PropertyCallbackInfo<Array> & info)412 static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
413   Environment* env = Environment::GetCurrent(info);
414   CHECK(env->has_run_bootstrapping_code());
415 
416   info.GetReturnValue().Set(
417       env->env_vars()->Enumerate(env->isolate()));
418 }
419 
EnvDefiner(Local<Name> property,const PropertyDescriptor & desc,const PropertyCallbackInfo<Value> & info)420 static void EnvDefiner(Local<Name> property,
421                        const PropertyDescriptor& desc,
422                        const PropertyCallbackInfo<Value>& info) {
423   Environment* env = Environment::GetCurrent(info);
424   if (desc.has_value()) {
425     if (!desc.has_writable() ||
426         !desc.has_enumerable() ||
427         !desc.has_configurable()) {
428       THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
429                                                "'process.env' only accepts a "
430                                                "configurable, writable,"
431                                                " and enumerable "
432                                                "data descriptor");
433     } else if (!desc.configurable() ||
434                !desc.enumerable() ||
435                !desc.writable()) {
436       THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
437                                                "'process.env' only accepts a "
438                                                "configurable, writable,"
439                                                " and enumerable "
440                                                "data descriptor");
441     } else {
442       return EnvSetter(property, desc.value(), info);
443     }
444   } else if (desc.has_get() || desc.has_set()) {
445     // we don't accept a getter/setter in 'process.env'
446     THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
447                                              "'process.env' does not accept an"
448                                              " accessor(getter/setter)"
449                                              " descriptor");
450   } else {
451     THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
452                                              "'process.env' only accepts a "
453                                              "configurable, writable,"
454                                              " and enumerable "
455                                              "data descriptor");
456   }
457 }
458 
CreateEnvProxyTemplate(Isolate * isolate,IsolateData * isolate_data)459 void CreateEnvProxyTemplate(Isolate* isolate, IsolateData* isolate_data) {
460   HandleScope scope(isolate);
461   if (!isolate_data->env_proxy_template().IsEmpty()) return;
462   Local<FunctionTemplate> env_proxy_ctor_template =
463       FunctionTemplate::New(isolate);
464   Local<ObjectTemplate> env_proxy_template =
465       ObjectTemplate::New(isolate, env_proxy_ctor_template);
466   env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
467       EnvGetter,
468       EnvSetter,
469       EnvQuery,
470       EnvDeleter,
471       EnvEnumerator,
472       EnvDefiner,
473       nullptr,
474       Local<Value>(),
475       PropertyHandlerFlags::kHasNoSideEffect));
476   isolate_data->set_env_proxy_template(env_proxy_template);
477   isolate_data->set_env_proxy_ctor_template(env_proxy_ctor_template);
478 }
479 
RegisterEnvVarExternalReferences(ExternalReferenceRegistry * registry)480 void RegisterEnvVarExternalReferences(ExternalReferenceRegistry* registry) {
481   registry->Register(EnvGetter);
482   registry->Register(EnvSetter);
483   registry->Register(EnvQuery);
484   registry->Register(EnvDeleter);
485   registry->Register(EnvEnumerator);
486   registry->Register(EnvDefiner);
487 }
488 }  // namespace node
489 
490 NODE_BINDING_EXTERNAL_REFERENCE(env_var, node::RegisterEnvVarExternalReferences)
491