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