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