• 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_process-inl.h"
5 
6 #include <time.h>  // tzset(), _tzset()
7 
8 namespace node {
9 using v8::Array;
10 using v8::Boolean;
11 using v8::Context;
12 using v8::DontDelete;
13 using v8::DontEnum;
14 using v8::EscapableHandleScope;
15 using v8::HandleScope;
16 using v8::Integer;
17 using v8::Isolate;
18 using v8::Just;
19 using v8::Local;
20 using v8::Maybe;
21 using v8::MaybeLocal;
22 using v8::Name;
23 using v8::NamedPropertyHandlerConfiguration;
24 using v8::NewStringType;
25 using v8::Nothing;
26 using v8::Object;
27 using v8::ObjectTemplate;
28 using v8::PropertyCallbackInfo;
29 using v8::PropertyHandlerFlags;
30 using v8::ReadOnly;
31 using v8::String;
32 using v8::Value;
33 
34 class RealEnvStore final : public KVStore {
35  public:
36   MaybeLocal<String> Get(Isolate* isolate, Local<String> key) const override;
37   Maybe<std::string> Get(const char* key) const override;
38   void Set(Isolate* isolate, Local<String> key, Local<String> value) override;
39   int32_t Query(Isolate* isolate, Local<String> key) const override;
40   int32_t Query(const char* key) const override;
41   void Delete(Isolate* isolate, Local<String> key) override;
42   Local<Array> Enumerate(Isolate* isolate) const override;
43 };
44 
45 class MapKVStore final : public KVStore {
46  public:
47   MaybeLocal<String> Get(Isolate* isolate, Local<String> key) const override;
48   Maybe<std::string> Get(const char* key) const override;
49   void Set(Isolate* isolate, Local<String> key, Local<String> value) override;
50   int32_t Query(Isolate* isolate, Local<String> key) const override;
51   int32_t Query(const char* key) const override;
52   void Delete(Isolate* isolate, Local<String> key) override;
53   Local<Array> Enumerate(Isolate* isolate) const override;
54 
55   std::shared_ptr<KVStore> Clone(Isolate* isolate) const override;
56 
57   MapKVStore() = default;
MapKVStore(const MapKVStore & other)58   MapKVStore(const MapKVStore& other) : KVStore(), map_(other.map_) {}
59 
60  private:
61   mutable Mutex mutex_;
62   std::unordered_map<std::string, std::string> map_;
63 };
64 
65 namespace per_process {
66 Mutex env_var_mutex;
67 std::shared_ptr<KVStore> system_environment = std::make_shared<RealEnvStore>();
68 }  // namespace per_process
69 
70 template <typename T>
DateTimeConfigurationChangeNotification(Isolate * isolate,const T & key)71 void DateTimeConfigurationChangeNotification(Isolate* isolate, const T& key) {
72   if (key.length() == 2 && key[0] == 'T' && key[1] == 'Z') {
73 #ifdef __POSIX__
74     tzset();
75 #else
76     _tzset();
77 #endif
78     auto constexpr time_zone_detection = Isolate::TimeZoneDetection::kRedetect;
79     isolate->DateTimeConfigurationChangeNotification(time_zone_detection);
80   }
81 }
82 
Get(const char * key) const83 Maybe<std::string> RealEnvStore::Get(const char* key) const {
84   Mutex::ScopedLock lock(per_process::env_var_mutex);
85 
86   size_t init_sz = 256;
87   MaybeStackBuffer<char, 256> val;
88   int ret = uv_os_getenv(key, *val, &init_sz);
89 
90   if (ret == UV_ENOBUFS) {
91     // Buffer is not large enough, reallocate to the updated init_sz
92     // and fetch env value again.
93     val.AllocateSufficientStorage(init_sz);
94     ret = uv_os_getenv(key, *val, &init_sz);
95   }
96 
97   if (ret >= 0) {  // Env key value fetch success.
98     return Just(std::string(*val, init_sz));
99   }
100 
101   return Nothing<std::string>();
102 }
103 
Get(Isolate * isolate,Local<String> property) const104 MaybeLocal<String> RealEnvStore::Get(Isolate* isolate,
105                                      Local<String> property) const {
106   node::Utf8Value key(isolate, property);
107   Maybe<std::string> value = Get(*key);
108 
109   if (value.IsJust()) {
110     std::string val = value.FromJust();
111     return String::NewFromUtf8(
112         isolate, val.data(), NewStringType::kNormal, val.size());
113   }
114 
115   return MaybeLocal<String>();
116 }
117 
Set(Isolate * isolate,Local<String> property,Local<String> value)118 void RealEnvStore::Set(Isolate* isolate,
119                        Local<String> property,
120                        Local<String> value) {
121   Mutex::ScopedLock lock(per_process::env_var_mutex);
122 
123   node::Utf8Value key(isolate, property);
124   node::Utf8Value val(isolate, value);
125 
126 #ifdef _WIN32
127   if (key.length() > 0 && key[0] == '=') return;
128 #endif
129   uv_os_setenv(*key, *val);
130   DateTimeConfigurationChangeNotification(isolate, key);
131 }
132 
Query(const char * key) const133 int32_t RealEnvStore::Query(const char* key) const {
134   Mutex::ScopedLock lock(per_process::env_var_mutex);
135 
136   char val[2];
137   size_t init_sz = sizeof(val);
138   int ret = uv_os_getenv(key, val, &init_sz);
139 
140   if (ret == UV_ENOENT) {
141     return -1;
142   }
143 
144 #ifdef _WIN32
145   if (key[0] == '=') {
146     return static_cast<int32_t>(ReadOnly) |
147            static_cast<int32_t>(DontDelete) |
148            static_cast<int32_t>(DontEnum);
149   }
150 #endif
151 
152   return 0;
153 }
154 
Query(Isolate * isolate,Local<String> property) const155 int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
156   node::Utf8Value key(isolate, property);
157   return Query(*key);
158 }
159 
Delete(Isolate * isolate,Local<String> property)160 void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
161   Mutex::ScopedLock lock(per_process::env_var_mutex);
162 
163   node::Utf8Value key(isolate, property);
164   uv_os_unsetenv(*key);
165   DateTimeConfigurationChangeNotification(isolate, key);
166 }
167 
Enumerate(Isolate * isolate) const168 Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {
169   Mutex::ScopedLock lock(per_process::env_var_mutex);
170   uv_env_item_t* items;
171   int count;
172 
173   auto cleanup = OnScopeLeave([&]() { uv_os_free_environ(items, count); });
174   CHECK_EQ(uv_os_environ(&items, &count), 0);
175 
176   MaybeStackBuffer<Local<Value>, 256> env_v(count);
177   int env_v_index = 0;
178   for (int i = 0; i < count; i++) {
179 #ifdef _WIN32
180     // If the key starts with '=' it is a hidden environment variable.
181     if (items[i].name[0] == '=') continue;
182 #endif
183     MaybeLocal<String> str = String::NewFromUtf8(isolate, items[i].name);
184     if (str.IsEmpty()) {
185       isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
186       return Local<Array>();
187     }
188     env_v[env_v_index++] = str.ToLocalChecked();
189   }
190 
191   return Array::New(isolate, env_v.out(), env_v_index);
192 }
193 
Clone(Isolate * isolate) const194 std::shared_ptr<KVStore> KVStore::Clone(Isolate* isolate) const {
195   HandleScope handle_scope(isolate);
196   Local<Context> context = isolate->GetCurrentContext();
197 
198   std::shared_ptr<KVStore> copy = KVStore::CreateMapKVStore();
199   Local<Array> keys = Enumerate(isolate);
200   uint32_t keys_length = keys->Length();
201   for (uint32_t i = 0; i < keys_length; i++) {
202     Local<Value> key = keys->Get(context, i).ToLocalChecked();
203     CHECK(key->IsString());
204     copy->Set(isolate,
205               key.As<String>(),
206               Get(isolate, key.As<String>()).ToLocalChecked());
207   }
208   return copy;
209 }
210 
Get(const char * key) const211 Maybe<std::string> MapKVStore::Get(const char* key) const {
212   Mutex::ScopedLock lock(mutex_);
213   auto it = map_.find(key);
214   return it == map_.end() ? Nothing<std::string>() : Just(it->second);
215 }
216 
Get(Isolate * isolate,Local<String> key) const217 MaybeLocal<String> MapKVStore::Get(Isolate* isolate, Local<String> key) const {
218   Utf8Value str(isolate, key);
219   Maybe<std::string> value = Get(*str);
220   if (value.IsNothing()) return Local<String>();
221   std::string val = value.FromJust();
222   return String::NewFromUtf8(
223       isolate, val.data(), NewStringType::kNormal, val.size());
224 }
225 
Set(Isolate * isolate,Local<String> key,Local<String> value)226 void MapKVStore::Set(Isolate* isolate, Local<String> key, Local<String> value) {
227   Mutex::ScopedLock lock(mutex_);
228   Utf8Value key_str(isolate, key);
229   Utf8Value value_str(isolate, value);
230   if (*key_str != nullptr && key_str.length() > 0 && *value_str != nullptr) {
231     map_[std::string(*key_str, key_str.length())] =
232         std::string(*value_str, value_str.length());
233   }
234 }
235 
Query(const char * key) const236 int32_t MapKVStore::Query(const char* key) const {
237   Mutex::ScopedLock lock(mutex_);
238   return map_.find(key) == map_.end() ? -1 : 0;
239 }
240 
Query(Isolate * isolate,Local<String> key) const241 int32_t MapKVStore::Query(Isolate* isolate, Local<String> key) const {
242   Utf8Value str(isolate, key);
243   return Query(*str);
244 }
245 
Delete(Isolate * isolate,Local<String> key)246 void MapKVStore::Delete(Isolate* isolate, Local<String> key) {
247   Mutex::ScopedLock lock(mutex_);
248   Utf8Value str(isolate, key);
249   map_.erase(std::string(*str, str.length()));
250 }
251 
Enumerate(Isolate * isolate) const252 Local<Array> MapKVStore::Enumerate(Isolate* isolate) const {
253   Mutex::ScopedLock lock(mutex_);
254   std::vector<Local<Value>> values;
255   values.reserve(map_.size());
256   for (const auto& pair : map_) {
257     values.emplace_back(
258         String::NewFromUtf8(isolate, pair.first.data(),
259                             NewStringType::kNormal, pair.first.size())
260             .ToLocalChecked());
261   }
262   return Array::New(isolate, values.data(), values.size());
263 }
264 
Clone(Isolate * isolate) const265 std::shared_ptr<KVStore> MapKVStore::Clone(Isolate* isolate) const {
266   return std::make_shared<MapKVStore>(*this);
267 }
268 
CreateMapKVStore()269 std::shared_ptr<KVStore> KVStore::CreateMapKVStore() {
270   return std::make_shared<MapKVStore>();
271 }
272 
AssignFromObject(Local<Context> context,Local<Object> entries)273 Maybe<bool> KVStore::AssignFromObject(Local<Context> context,
274                                       Local<Object> entries) {
275   Isolate* isolate = context->GetIsolate();
276   HandleScope handle_scope(isolate);
277   Local<Array> keys;
278   if (!entries->GetOwnPropertyNames(context).ToLocal(&keys))
279     return Nothing<bool>();
280   uint32_t keys_length = keys->Length();
281   for (uint32_t i = 0; i < keys_length; i++) {
282     Local<Value> key;
283     if (!keys->Get(context, i).ToLocal(&key))
284       return Nothing<bool>();
285     if (!key->IsString()) continue;
286 
287     Local<Value> value;
288     Local<String> value_string;
289     if (!entries->Get(context, key).ToLocal(&value) ||
290         !value->ToString(context).ToLocal(&value_string)) {
291       return Nothing<bool>();
292     }
293 
294     Set(isolate, key.As<String>(), value_string);
295   }
296   return Just(true);
297 }
298 
EnvGetter(Local<Name> property,const PropertyCallbackInfo<Value> & info)299 static void EnvGetter(Local<Name> property,
300                       const PropertyCallbackInfo<Value>& info) {
301   Environment* env = Environment::GetCurrent(info);
302   CHECK(env->has_run_bootstrapping_code());
303   if (property->IsSymbol()) {
304     return info.GetReturnValue().SetUndefined();
305   }
306   CHECK(property->IsString());
307   MaybeLocal<String> value_string =
308       env->env_vars()->Get(env->isolate(), property.As<String>());
309   if (!value_string.IsEmpty()) {
310     info.GetReturnValue().Set(value_string.ToLocalChecked());
311   }
312 }
313 
EnvSetter(Local<Name> property,Local<Value> value,const PropertyCallbackInfo<Value> & info)314 static void EnvSetter(Local<Name> property,
315                       Local<Value> value,
316                       const PropertyCallbackInfo<Value>& info) {
317   Environment* env = Environment::GetCurrent(info);
318   CHECK(env->has_run_bootstrapping_code());
319   // calling env->EmitProcessEnvWarning() sets a variable indicating that
320   // warnings have been emitted. It should be called last after other
321   // conditions leading to a warning have been met.
322   if (env->options()->pending_deprecation && !value->IsString() &&
323       !value->IsNumber() && !value->IsBoolean() &&
324       env->EmitProcessEnvWarning()) {
325     if (ProcessEmitDeprecationWarning(
326             env,
327             "Assigning any value other than a string, number, or boolean to a "
328             "process.env property is deprecated. Please make sure to convert "
329             "the "
330             "value to a string before setting process.env with it.",
331             "DEP0104")
332             .IsNothing())
333       return;
334   }
335 
336   Local<String> key;
337   Local<String> value_string;
338   if (!property->ToString(env->context()).ToLocal(&key) ||
339       !value->ToString(env->context()).ToLocal(&value_string)) {
340     return;
341   }
342 
343   env->env_vars()->Set(env->isolate(), key, value_string);
344 
345   // Whether it worked or not, always return value.
346   info.GetReturnValue().Set(value);
347 }
348 
EnvQuery(Local<Name> property,const PropertyCallbackInfo<Integer> & info)349 static void EnvQuery(Local<Name> property,
350                      const PropertyCallbackInfo<Integer>& info) {
351   Environment* env = Environment::GetCurrent(info);
352   CHECK(env->has_run_bootstrapping_code());
353   if (property->IsString()) {
354     int32_t rc = env->env_vars()->Query(env->isolate(), property.As<String>());
355     if (rc != -1) info.GetReturnValue().Set(rc);
356   }
357 }
358 
EnvDeleter(Local<Name> property,const PropertyCallbackInfo<Boolean> & info)359 static void EnvDeleter(Local<Name> property,
360                        const PropertyCallbackInfo<Boolean>& info) {
361   Environment* env = Environment::GetCurrent(info);
362   CHECK(env->has_run_bootstrapping_code());
363   if (property->IsString()) {
364     env->env_vars()->Delete(env->isolate(), property.As<String>());
365   }
366 
367   // process.env never has non-configurable properties, so always
368   // return true like the tc39 delete operator.
369   info.GetReturnValue().Set(true);
370 }
371 
EnvEnumerator(const PropertyCallbackInfo<Array> & info)372 static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
373   Environment* env = Environment::GetCurrent(info);
374   CHECK(env->has_run_bootstrapping_code());
375 
376   info.GetReturnValue().Set(
377       env->env_vars()->Enumerate(env->isolate()));
378 }
379 
CreateEnvVarProxy(Local<Context> context,Isolate * isolate)380 MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, Isolate* isolate) {
381   EscapableHandleScope scope(isolate);
382   Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate);
383   env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
384       EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, Local<Value>(),
385       PropertyHandlerFlags::kHasNoSideEffect));
386   return scope.EscapeMaybe(env_proxy_template->NewInstance(context));
387 }
388 }  // namespace node
389