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