GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_env_var.cc Lines: 192 202 95.0 %
Date: 2022-06-17 04:15:42 Branches: 93 128 72.7 %

Line Branch Exec Source
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::EscapableHandleScope;
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
1025
  MapKVStore() = default;
61
10
  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>
74
8728
void DateTimeConfigurationChangeNotification(
75
    Isolate* isolate,
76
    const T& key,
77
    const char* val = nullptr) {
78


8728
  if (key.length() == 2 && key[0] == 'T' && key[1] == 'Z') {
79
#ifdef __POSIX__
80
66
    tzset();
81
66
    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
8728
}
102
103
646081
Maybe<std::string> RealEnvStore::Get(const char* key) const {
104
1292162
  Mutex::ScopedLock lock(per_process::env_var_mutex);
105
106
646081
  size_t init_sz = 256;
107
1292162
  MaybeStackBuffer<char, 256> val;
108
646081
  int ret = uv_os_getenv(key, *val, &init_sz);
109
110
646081
  if (ret == UV_ENOBUFS) {
111
    // Buffer is not large enough, reallocate to the updated init_sz
112
    // and fetch env value again.
113
7012
    val.AllocateSufficientStorage(init_sz);
114
7012
    ret = uv_os_getenv(key, *val, &init_sz);
115
  }
116
117
646081
  if (ret >= 0) {  // Env key value fetch success.
118
579096
    return Just(std::string(*val, init_sz));
119
  }
120
121
66985
  return Nothing<std::string>();
122
}
123
124
646081
MaybeLocal<String> RealEnvStore::Get(Isolate* isolate,
125
                                     Local<String> property) const {
126
1292162
  node::Utf8Value key(isolate, property);
127
1292162
  Maybe<std::string> value = Get(*key);
128
129
646081
  if (value.IsJust()) {
130
579096
    std::string val = value.FromJust();
131
    return String::NewFromUtf8(
132
579096
        isolate, val.data(), NewStringType::kNormal, val.size());
133
  }
134
135
66985
  return MaybeLocal<String>();
136
}
137
138
7686
void RealEnvStore::Set(Isolate* isolate,
139
                       Local<String> property,
140
                       Local<String> value) {
141
15372
  Mutex::ScopedLock lock(per_process::env_var_mutex);
142
143
15372
  node::Utf8Value key(isolate, property);
144
15372
  node::Utf8Value val(isolate, value);
145
146
#ifdef _WIN32
147
  if (key.length() > 0 && key[0] == '=') return;
148
#endif
149
7686
  uv_os_setenv(*key, *val);
150
7686
  DateTimeConfigurationChangeNotification(isolate, key, *val);
151
7686
}
152
153
702021
int32_t RealEnvStore::Query(const char* key) const {
154
1404042
  Mutex::ScopedLock lock(per_process::env_var_mutex);
155
156
  char val[2];
157
702021
  size_t init_sz = sizeof(val);
158
702021
  int ret = uv_os_getenv(key, val, &init_sz);
159
160
702021
  if (ret == UV_ENOENT) {
161
6123
    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
695898
  return 0;
173
}
174
175
702021
int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
176
1404042
  node::Utf8Value key(isolate, property);
177
702021
  return Query(*key);
178
}
179
180
1042
void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
181
2084
  Mutex::ScopedLock lock(per_process::env_var_mutex);
182
183
2084
  node::Utf8Value key(isolate, property);
184
1042
  uv_os_unsetenv(*key);
185
1042
  DateTimeConfigurationChangeNotification(isolate, key);
186
1042
}
187
188
8611
Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {
189
17222
  Mutex::ScopedLock lock(per_process::env_var_mutex);
190
  uv_env_item_t* items;
191
  int count;
192
193
25833
  auto cleanup = OnScopeLeave([&]() { uv_os_free_environ(items, count); });
194
8611
  CHECK_EQ(uv_os_environ(&items, &count), 0);
195
196
17222
  MaybeStackBuffer<Local<Value>, 256> env_v(count);
197
8611
  int env_v_index = 0;
198
637567
  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
628956
    MaybeLocal<String> str = String::NewFromUtf8(isolate, items[i].name);
204
628956
    if (str.IsEmpty()) {
205
      isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
206
      return Local<Array>();
207
    }
208
628956
    env_v[env_v_index++] = str.ToLocalChecked();
209
  }
210
211
8611
  return Array::New(isolate, env_v.out(), env_v_index);
212
}
213
214
999
std::shared_ptr<KVStore> KVStore::Clone(Isolate* isolate) const {
215
1998
  HandleScope handle_scope(isolate);
216
999
  Local<Context> context = isolate->GetCurrentContext();
217
218
999
  std::shared_ptr<KVStore> copy = KVStore::CreateMapKVStore();
219
999
  Local<Array> keys = Enumerate(isolate);
220
999
  uint32_t keys_length = keys->Length();
221
73184
  for (uint32_t i = 0; i < keys_length; i++) {
222
144370
    Local<Value> key = keys->Get(context, i).ToLocalChecked();
223
144370
    CHECK(key->IsString());
224
144370
    copy->Set(isolate,
225
              key.As<String>(),
226
216555
              Get(isolate, key.As<String>()).ToLocalChecked());
227
  }
228
999
  return copy;
229
}
230
231
38880
Maybe<std::string> MapKVStore::Get(const char* key) const {
232
38880
  Mutex::ScopedLock lock(mutex_);
233
38880
  auto it = map_.find(key);
234
38880
  return it == map_.end() ? Nothing<std::string>() : Just(it->second);
235
}
236
237
37776
MaybeLocal<String> MapKVStore::Get(Isolate* isolate, Local<String> key) const {
238
75552
  Utf8Value str(isolate, key);
239
75552
  Maybe<std::string> value = Get(*str);
240
42836
  if (value.IsNothing()) return Local<String>();
241
32716
  std::string val = value.FromJust();
242
  return String::NewFromUtf8(
243
32716
      isolate, val.data(), NewStringType::kNormal, val.size());
244
}
245
246
73924
void MapKVStore::Set(Isolate* isolate, Local<String> key, Local<String> value) {
247
147848
  Mutex::ScopedLock lock(mutex_);
248
147848
  Utf8Value key_str(isolate, key);
249
147848
  Utf8Value value_str(isolate, value);
250


73924
  if (*key_str != nullptr && key_str.length() > 0 && *value_str != nullptr) {
251
147848
    map_[std::string(*key_str, key_str.length())] =
252
221772
        std::string(*value_str, value_str.length());
253
  }
254
73924
}
255
256
53869
int32_t MapKVStore::Query(const char* key) const {
257
53869
  Mutex::ScopedLock lock(mutex_);
258
53869
  return map_.find(key) == map_.end() ? -1 : 0;
259
}
260
261
53869
int32_t MapKVStore::Query(Isolate* isolate, Local<String> key) const {
262
107738
  Utf8Value str(isolate, key);
263
53869
  return Query(*str);
264
}
265
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
272
722
Local<Array> MapKVStore::Enumerate(Isolate* isolate) const {
273
1444
  Mutex::ScopedLock lock(mutex_);
274
1444
  std::vector<Local<Value>> values;
275
722
  values.reserve(map_.size());
276
53867
  for (const auto& pair : map_) {
277
    values.emplace_back(
278
53145
        String::NewFromUtf8(isolate, pair.first.data(),
279
53145
                            NewStringType::kNormal, pair.first.size())
280
53145
            .ToLocalChecked());
281
  }
282
722
  return Array::New(isolate, values.data(), values.size());
283
}
284
285
10
std::shared_ptr<KVStore> MapKVStore::Clone(Isolate* isolate) const {
286
10
  return std::make_shared<MapKVStore>(*this);
287
}
288
289
1025
std::shared_ptr<KVStore> KVStore::CreateMapKVStore() {
290
1025
  return std::make_shared<MapKVStore>();
291
}
292
293
26
Maybe<bool> KVStore::AssignFromObject(Local<Context> context,
294
                                      Local<Object> entries) {
295
26
  Isolate* isolate = context->GetIsolate();
296
52
  HandleScope handle_scope(isolate);
297
  Local<Array> keys;
298
52
  if (!entries->GetOwnPropertyNames(context).ToLocal(&keys))
299
    return Nothing<bool>();
300
26
  uint32_t keys_length = keys->Length();
301
1758
  for (uint32_t i = 0; i < keys_length; i++) {
302
    Local<Value> key;
303
3464
    if (!keys->Get(context, i).ToLocal(&key))
304
      return Nothing<bool>();
305
3464
    if (!key->IsString()) continue;
306
307
    Local<Value> value;
308
    Local<String> value_string;
309
5196
    if (!entries->Get(context, key).ToLocal(&value) ||
310

5196
        !value->ToString(context).ToLocal(&value_string)) {
311
      return Nothing<bool>();
312
    }
313
314
3464
    Set(isolate, key.As<String>(), value_string);
315
  }
316
26
  return Just(true);
317
}
318
319
594918
static void EnvGetter(Local<Name> property,
320
                      const PropertyCallbackInfo<Value>& info) {
321
594918
  Environment* env = Environment::GetCurrent(info);
322
594918
  CHECK(env->has_run_bootstrapping_code());
323
594918
  if (property->IsSymbol()) {
324
16044
    return info.GetReturnValue().SetUndefined();
325
  }
326
1173792
  CHECK(property->IsString());
327
  MaybeLocal<String> value_string =
328
1173792
      env->env_vars()->Get(env->isolate(), property.As<String>());
329
586896
  if (!value_string.IsEmpty()) {
330
1054306
    info.GetReturnValue().Set(value_string.ToLocalChecked());
331
  }
332
}
333
334
7695
static void EnvSetter(Local<Name> property,
335
                      Local<Value> value,
336
                      const PropertyCallbackInfo<Value>& info) {
337
7695
  Environment* env = Environment::GetCurrent(info);
338
7695
  CHECK(env->has_run_bootstrapping_code());
339
  // calling env->EmitProcessEnvWarning() sets a variable indicating that
340
  // warnings have been emitted. It should be called last after other
341
  // conditions leading to a warning have been met.
342

7735
  if (env->options()->pending_deprecation && !value->IsString() &&
343


7717
      !value->IsNumber() && !value->IsBoolean() &&
344
1
      env->EmitProcessEnvWarning()) {
345
1
    if (ProcessEmitDeprecationWarning(
346
            env,
347
            "Assigning any value other than a string, number, or boolean to a "
348
            "process.env property is deprecated. Please make sure to convert "
349
            "the "
350
            "value to a string before setting process.env with it.",
351
1
            "DEP0104")
352
1
            .IsNothing())
353
2
      return;
354
  }
355
356
  Local<String> key;
357
  Local<String> value_string;
358
23084
  if (!property->ToString(env->context()).ToLocal(&key) ||
359

23083
      !value->ToString(env->context()).ToLocal(&value_string)) {
360
2
    return;
361
  }
362
363
7693
  env->env_vars()->Set(env->isolate(), key, value_string);
364
365
  // Whether it worked or not, always return value.
366
15386
  info.GetReturnValue().Set(value);
367
}
368
369
755891
static void EnvQuery(Local<Name> property,
370
                     const PropertyCallbackInfo<Integer>& info) {
371
755891
  Environment* env = Environment::GetCurrent(info);
372
755891
  CHECK(env->has_run_bootstrapping_code());
373
1511782
  if (property->IsString()) {
374
1511780
    int32_t rc = env->env_vars()->Query(env->isolate(), property.As<String>());
375
2254700
    if (rc != -1) info.GetReturnValue().Set(rc);
376
  }
377
755891
}
378
379
1043
static void EnvDeleter(Local<Name> property,
380
                       const PropertyCallbackInfo<Boolean>& info) {
381
1043
  Environment* env = Environment::GetCurrent(info);
382
1043
  CHECK(env->has_run_bootstrapping_code());
383
2086
  if (property->IsString()) {
384
2084
    env->env_vars()->Delete(env->isolate(), property.As<String>());
385
  }
386
387
  // process.env never has non-configurable properties, so always
388
  // return true like the tc39 delete operator.
389
1043
  info.GetReturnValue().Set(true);
390
1043
}
391
392
8334
static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
393
8334
  Environment* env = Environment::GetCurrent(info);
394
8334
  CHECK(env->has_run_bootstrapping_code());
395
396
8334
  info.GetReturnValue().Set(
397
16668
      env->env_vars()->Enumerate(env->isolate()));
398
8334
}
399
400
7
static void EnvDefiner(Local<Name> property,
401
                       const PropertyDescriptor& desc,
402
                       const PropertyCallbackInfo<Value>& info) {
403
7
  Environment* env = Environment::GetCurrent(info);
404
7
  if (desc.has_value()) {
405
3
    if (!desc.has_writable() ||
406

4
        !desc.has_enumerable() ||
407
1
        !desc.has_configurable()) {
408
2
      THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
409
                                               "'process.env' only accepts a "
410
                                               "configurable, writable,"
411
                                               " and enumerable "
412
                                               "data descriptor");
413
1
    } else if (!desc.configurable() ||
414

2
               !desc.enumerable() ||
415
1
               !desc.writable()) {
416
      THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
417
                                               "'process.env' only accepts a "
418
                                               "configurable, writable,"
419
                                               " and enumerable "
420
                                               "data descriptor");
421
    } else {
422
1
      return EnvSetter(property, desc.value(), info);
423
    }
424

4
  } else if (desc.has_get() || desc.has_set()) {
425
    // we don't accept a getter/setter in 'process.env'
426
1
    THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
427
                             "'process.env' does not accept an"
428
                                             "accessor(getter/setter)"
429
                                             " descriptor");
430
  } else {
431
3
    THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
432
                                             "'process.env' only accepts a "
433
                                             "configurable, writable,"
434
                                             " and enumerable "
435
                                             "data descriptor");
436
  }
437
}
438
439
852
MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, Isolate* isolate) {
440
852
  EscapableHandleScope scope(isolate);
441
852
  Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate);
442
1704
  env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
443
      EnvGetter,
444
      EnvSetter,
445
      EnvQuery,
446
      EnvDeleter,
447
      EnvEnumerator,
448
      EnvDefiner,
449
      nullptr,
450
      Local<Value>(),
451
      PropertyHandlerFlags::kHasNoSideEffect));
452
1704
  return scope.EscapeMaybe(env_proxy_template->NewInstance(context));
453
}
454
455
5204
void RegisterEnvVarExternalReferences(ExternalReferenceRegistry* registry) {
456
5204
  registry->Register(EnvGetter);
457
5204
  registry->Register(EnvSetter);
458
5204
  registry->Register(EnvQuery);
459
5204
  registry->Register(EnvDeleter);
460
5204
  registry->Register(EnvEnumerator);
461
5204
  registry->Register(EnvDefiner);
462
5204
}
463
}  // namespace node
464
465
5204
NODE_MODULE_EXTERNAL_REFERENCE(env_var, node::RegisterEnvVarExternalReferences)