aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/errors.md5
-rw-r--r--lib/internal/bootstrap/node.js1
-rw-r--r--lib/internal/errors.js1
-rw-r--r--lib/internal/process/methods.js137
-rw-r--r--node.gyp1
-rw-r--r--src/node.cc126
-rw-r--r--test/parallel/test-process-chdir.js15
-rw-r--r--test/parallel/test-process-euid-egid.js19
-rw-r--r--test/parallel/test-process-uid-gid.js17
-rw-r--r--test/parallel/test-process-umask.js (renamed from test/parallel/test-umask.js)17
10 files changed, 246 insertions, 93 deletions
diff --git a/doc/api/errors.md b/doc/api/errors.md
index ff317f676a..84db161af8 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -1607,6 +1607,11 @@ A string that contained unescaped characters was received.
An unhandled error occurred (for instance, when an `'error'` event is emitted
by an [`EventEmitter`][] but an `'error'` handler is not registered).
+<a id="ERR_UNKNOWN_CREDENTIAL"></a>
+### ERR_UNKNOWN_CREDENTIAL
+
+A Unix group or user identifier that does not exist was passed.
+
<a id="ERR_UNKNOWN_ENCODING"></a>
### ERR_UNKNOWN_ENCODING
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index f9bed03d26..5993cc71f7 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -41,6 +41,7 @@
NativeModule.require('internal/process/warning').setup();
NativeModule.require('internal/process/next_tick').setup();
NativeModule.require('internal/process/stdio').setup();
+ NativeModule.require('internal/process/methods').setup();
const perf = process.binding('performance');
const {
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index e7eb3bd133..882e40af88 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1017,6 +1017,7 @@ E('ERR_UNHANDLED_ERROR',
if (err === undefined) return msg;
return `${msg} (${err})`;
}, Error);
+E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error);
E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError);
// This should probably be a `TypeError`.
diff --git a/lib/internal/process/methods.js b/lib/internal/process/methods.js
new file mode 100644
index 0000000000..503fd317f6
--- /dev/null
+++ b/lib/internal/process/methods.js
@@ -0,0 +1,137 @@
+'use strict';
+
+const {
+ ERR_INVALID_ARG_TYPE,
+ ERR_INVALID_ARG_VALUE,
+ ERR_UNKNOWN_CREDENTIAL
+} = require('internal/errors').codes;
+const {
+ validateUint32
+} = require('internal/validators');
+
+function setupProcessMethods() {
+ // Non-POSIX platforms like Windows don't have certain methods.
+ if (process.setgid !== undefined) {
+ setupPosixMethods();
+ }
+
+ const {
+ chdir: _chdir,
+ umask: _umask,
+ } = process;
+
+ process.chdir = chdir;
+ process.umask = umask;
+
+ function chdir(directory) {
+ if (typeof directory !== 'string') {
+ throw new ERR_INVALID_ARG_TYPE('directory', 'string', directory);
+ }
+ return _chdir(directory);
+ }
+
+ const octalReg = /^[0-7]+$/;
+ function umask(mask) {
+ if (typeof mask === 'undefined') {
+ return _umask(mask);
+ }
+
+ if (typeof mask === 'number') {
+ validateUint32(mask, 'mask');
+ return _umask(mask);
+ }
+
+ if (typeof mask === 'string') {
+ if (!octalReg.test(mask)) {
+ throw new ERR_INVALID_ARG_VALUE('mask', mask,
+ 'must be an octal string');
+ }
+ const octal = Number.parseInt(mask, 8);
+ validateUint32(octal, 'mask');
+ return _umask(octal);
+ }
+
+ throw new ERR_INVALID_ARG_TYPE('mask', ['number', 'string', 'undefined'],
+ mask);
+ }
+}
+
+function setupPosixMethods() {
+ const {
+ initgroups: _initgroups,
+ setegid: _setegid,
+ seteuid: _seteuid,
+ setgid: _setgid,
+ setuid: _setuid,
+ setgroups: _setgroups
+ } = process;
+
+ process.initgroups = initgroups;
+ process.setegid = setegid;
+ process.seteuid = seteuid;
+ process.setgid = setgid;
+ process.setuid = setuid;
+ process.setgroups = setgroups;
+
+ function initgroups(user, extraGroup) {
+ validateId(user, 'user');
+ validateId(extraGroup, 'extraGroup');
+ // Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
+ const result = _initgroups(user, extraGroup);
+ if (result === 1) {
+ throw new ERR_UNKNOWN_CREDENTIAL('User', user);
+ } else if (result === 2) {
+ throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
+ }
+ }
+
+ function setegid(id) {
+ return execId(id, 'Group', _setegid);
+ }
+
+ function seteuid(id) {
+ return execId(id, 'User', _seteuid);
+ }
+
+ function setgid(id) {
+ return execId(id, 'Group', _setgid);
+ }
+
+ function setuid(id) {
+ return execId(id, 'User', _setuid);
+ }
+
+ function setgroups(groups) {
+ if (!Array.isArray(groups)) {
+ throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
+ }
+ for (var i = 0; i < groups.length; i++) {
+ validateId(groups[i], `groups[${i}]`);
+ }
+ // Result is 0 on success. A positive integer indicates that the
+ // corresponding group was not found.
+ const result = _setgroups(groups);
+ if (result > 0) {
+ throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
+ }
+ }
+
+ function execId(id, type, method) {
+ validateId(id, 'id');
+ // Result is 0 on success, 1 if credential is unknown.
+ const result = method(id);
+ if (result === 1) {
+ throw new ERR_UNKNOWN_CREDENTIAL(type, id);
+ }
+ }
+
+ function validateId(id, name) {
+ if (typeof id === 'number') {
+ validateUint32(id, name);
+ } else if (typeof id !== 'string') {
+ throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
+ }
+ }
+}
+
+exports.setup = setupProcessMethods;
diff --git a/node.gyp b/node.gyp
index 2c7eb994d2..ba65eafee9 100644
--- a/node.gyp
+++ b/node.gyp
@@ -118,6 +118,7 @@
'lib/internal/net.js',
'lib/internal/os.js',
'lib/internal/process/esm_loader.js',
+ 'lib/internal/process/methods.js',
'lib/internal/process/next_tick.js',
'lib/internal/process/promises.js',
'lib/internal/process/stdio.js',
diff --git a/src/node.cc b/src/node.cc
index 032963bff4..07dc4f13fd 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -164,6 +164,7 @@ using v8::ScriptOrigin;
using v8::SealHandleScope;
using v8::String;
using v8::TryCatch;
+using v8::Uint32;
using v8::Uint32Array;
using v8::Undefined;
using v8::V8;
@@ -1580,10 +1581,8 @@ static void Abort(const FunctionCallbackInfo<Value>& args) {
static void Chdir(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (args.Length() != 1 || !args[0]->IsString()) {
- return env->ThrowTypeError("Bad argument.");
- }
-
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsString());
node::Utf8Value path(args.GetIsolate(), args[0]);
int err = uv_chdir(*path);
if (err) {
@@ -1616,32 +1615,16 @@ static void Cwd(const FunctionCallbackInfo<Value>& args) {
static void Umask(const FunctionCallbackInfo<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
uint32_t old;
- if (args.Length() < 1 || args[0]->IsUndefined()) {
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsUndefined() || args[0]->IsUint32());
+
+ if (args[0]->IsUndefined()) {
old = umask(0);
umask(static_cast<mode_t>(old));
- } else if (!args[0]->IsInt32() && !args[0]->IsString()) {
- return env->ThrowTypeError("argument must be an integer or octal string.");
} else {
- int oct;
- if (args[0]->IsInt32()) {
- oct = args[0]->Uint32Value();
- } else {
- oct = 0;
- node::Utf8Value str(env->isolate(), args[0]);
-
- // Parse the octal string.
- for (size_t i = 0; i < str.length(); i++) {
- char c = (*str)[i];
- if (c > '7' || c < '0') {
- return env->ThrowTypeError("invalid octal string");
- }
- oct *= 8;
- oct += c - '0';
- }
- }
+ int oct = args[0].As<Uint32>()->Value();
old = umask(static_cast<mode_t>(oct));
}
@@ -1779,18 +1762,18 @@ static void GetEGid(const FunctionCallbackInfo<Value>& args) {
static void SetGid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (!args[0]->IsUint32() && !args[0]->IsString()) {
- return env->ThrowTypeError("setgid argument must be a number or a string");
- }
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsUint32() || args[0]->IsString());
gid_t gid = gid_by_name(env->isolate(), args[0]);
if (gid == gid_not_found) {
- return env->ThrowError("setgid group id does not exist");
- }
-
- if (setgid(gid)) {
- return env->ThrowErrnoException(errno, "setgid");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ args.GetReturnValue().Set(1);
+ } else if (setgid(gid)) {
+ env->ThrowErrnoException(errno, "setgid");
+ } else {
+ args.GetReturnValue().Set(0);
}
}
@@ -1798,18 +1781,18 @@ static void SetGid(const FunctionCallbackInfo<Value>& args) {
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (!args[0]->IsUint32() && !args[0]->IsString()) {
- return env->ThrowTypeError("setegid argument must be a number or string");
- }
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsUint32() || args[0]->IsString());
gid_t gid = gid_by_name(env->isolate(), args[0]);
if (gid == gid_not_found) {
- return env->ThrowError("setegid group id does not exist");
- }
-
- if (setegid(gid)) {
- return env->ThrowErrnoException(errno, "setegid");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ args.GetReturnValue().Set(1);
+ } else if (setegid(gid)) {
+ env->ThrowErrnoException(errno, "setegid");
+ } else {
+ args.GetReturnValue().Set(0);
}
}
@@ -1817,18 +1800,18 @@ static void SetEGid(const FunctionCallbackInfo<Value>& args) {
static void SetUid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (!args[0]->IsUint32() && !args[0]->IsString()) {
- return env->ThrowTypeError("setuid argument must be a number or a string");
- }
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsUint32() || args[0]->IsString());
uid_t uid = uid_by_name(env->isolate(), args[0]);
if (uid == uid_not_found) {
- return env->ThrowError("setuid user id does not exist");
- }
-
- if (setuid(uid)) {
- return env->ThrowErrnoException(errno, "setuid");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ args.GetReturnValue().Set(1);
+ } else if (setuid(uid)) {
+ env->ThrowErrnoException(errno, "setuid");
+ } else {
+ args.GetReturnValue().Set(0);
}
}
@@ -1836,18 +1819,18 @@ static void SetUid(const FunctionCallbackInfo<Value>& args) {
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (!args[0]->IsUint32() && !args[0]->IsString()) {
- return env->ThrowTypeError("seteuid argument must be a number or string");
- }
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsUint32() || args[0]->IsString());
uid_t uid = uid_by_name(env->isolate(), args[0]);
if (uid == uid_not_found) {
- return env->ThrowError("seteuid user id does not exist");
- }
-
- if (seteuid(uid)) {
- return env->ThrowErrnoException(errno, "seteuid");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ args.GetReturnValue().Set(1);
+ } else if (seteuid(uid)) {
+ env->ThrowErrnoException(errno, "seteuid");
+ } else {
+ args.GetReturnValue().Set(0);
}
}
@@ -1893,9 +1876,8 @@ static void GetGroups(const FunctionCallbackInfo<Value>& args) {
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (!args[0]->IsArray()) {
- return env->ThrowTypeError("argument 1 must be an array");
- }
+ CHECK_EQ(args.Length(), 1);
+ CHECK(args[0]->IsArray());
Local<Array> groups_list = args[0].As<Array>();
size_t size = groups_list->Length();
@@ -1906,7 +1888,9 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
if (gid == gid_not_found) {
delete[] groups;
- return env->ThrowError("group name not found");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
+ return;
}
groups[i] = gid;
@@ -1918,19 +1902,17 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
if (rc == -1) {
return env->ThrowErrnoException(errno, "setgroups");
}
+
+ args.GetReturnValue().Set(0);
}
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (!args[0]->IsUint32() && !args[0]->IsString()) {
- return env->ThrowTypeError("argument 1 must be a number or a string");
- }
-
- if (!args[1]->IsUint32() && !args[1]->IsString()) {
- return env->ThrowTypeError("argument 2 must be a number or a string");
- }
+ CHECK_EQ(args.Length(), 2);
+ CHECK(args[0]->IsUint32() || args[0]->IsString());
+ CHECK(args[1]->IsUint32() || args[1]->IsString());
node::Utf8Value arg0(env->isolate(), args[0]);
gid_t extra_group;
@@ -1946,7 +1928,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
}
if (user == nullptr) {
- return env->ThrowError("initgroups user not found");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ return args.GetReturnValue().Set(1);
}
extra_group = gid_by_name(env->isolate(), args[1]);
@@ -1954,7 +1937,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
if (extra_group == gid_not_found) {
if (must_free)
free(user);
- return env->ThrowError("initgroups extra group not found");
+ // Tells JS to throw ERR_INVALID_CREDENTIAL
+ return args.GetReturnValue().Set(2);
}
int rc = initgroups(user, extra_group);
@@ -1966,6 +1950,8 @@ static void InitGroups(const FunctionCallbackInfo<Value>& args) {
if (rc) {
return env->ThrowErrnoException(errno, "initgroups");
}
+
+ args.GetReturnValue().Set(0);
}
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
diff --git a/test/parallel/test-process-chdir.js b/test/parallel/test-process-chdir.js
index c0a245ffd3..998147dd43 100644
--- a/test/parallel/test-process-chdir.js
+++ b/test/parallel/test-process-chdir.js
@@ -1,6 +1,6 @@
'use strict';
-require('../common');
+const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
@@ -33,10 +33,9 @@ process.chdir('..');
assert.strictEqual(process.cwd().normalize(),
path.resolve(tmpdir.path).normalize());
-const errMessage = /^TypeError: Bad argument\.$/;
-assert.throws(function() { process.chdir({}); },
- errMessage, 'Bad argument.');
-assert.throws(function() { process.chdir(); },
- errMessage, 'Bad argument.');
-assert.throws(function() { process.chdir('x', 'y'); },
- errMessage, 'Bad argument.');
+const err = {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "directory" argument must be of type string/
+};
+common.expectsError(function() { process.chdir({}); }, err);
+common.expectsError(function() { process.chdir(); }, err);
diff --git a/test/parallel/test-process-euid-egid.js b/test/parallel/test-process-euid-egid.js
index adae58abeb..84fbd03e32 100644
--- a/test/parallel/test-process-euid-egid.js
+++ b/test/parallel/test-process-euid-egid.js
@@ -13,11 +13,18 @@ if (common.isWindows) {
assert.throws(() => {
process.seteuid({});
-}, /^TypeError: seteuid argument must be a number or string$/);
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: 'The "id" argument must be one of type number or string. ' +
+ 'Received type object'
+});
assert.throws(() => {
- process.seteuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf');
-}, /^Error: seteuid user id does not exist$/);
+ process.seteuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveyb');
+}, {
+ code: 'ERR_UNKNOWN_CREDENTIAL',
+ message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'
+});
// If we're not running as super user...
if (process.getuid() !== 0) {
@@ -27,11 +34,11 @@ if (process.getuid() !== 0) {
assert.throws(() => {
process.setegid('nobody');
- }, /^Error: (?:EPERM, .+|setegid group id does not exist)$/);
+ }, /(?:EPERM, .+|Group identifier does not exist: nobody)$/);
assert.throws(() => {
process.seteuid('nobody');
- }, /^Error: (?:EPERM, .+|seteuid user id does not exist)$/);
+ }, /^Error: (?:EPERM, .+|User identifier does not exist: nobody)$/);
return;
}
@@ -41,7 +48,7 @@ const oldgid = process.getegid();
try {
process.setegid('nobody');
} catch (err) {
- if (err.message !== 'setegid group id does not exist') {
+ if (err.message !== 'Group identifier does not exist: nobody') {
throw err;
} else {
process.setegid('nogroup');
diff --git a/test/parallel/test-process-uid-gid.js b/test/parallel/test-process-uid-gid.js
index d044c45a32..2475194309 100644
--- a/test/parallel/test-process-uid-gid.js
+++ b/test/parallel/test-process-uid-gid.js
@@ -35,11 +35,18 @@ if (common.isWindows) {
assert.throws(() => {
process.setuid({});
-}, /^TypeError: setuid argument must be a number or a string$/);
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: 'The "id" argument must be one of type ' +
+ 'number or string. Received type object'
+});
assert.throws(() => {
- process.setuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf');
-}, /^Error: setuid user id does not exist$/);
+ process.setuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveyb');
+}, {
+ code: 'ERR_UNKNOWN_CREDENTIAL',
+ message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'
+});
// If we're not running as super user...
if (process.getuid() !== 0) {
@@ -49,12 +56,12 @@ if (process.getuid() !== 0) {
assert.throws(
() => { process.setgid('nobody'); },
- /^Error: (?:EPERM, .+|setgid group id does not exist)$/
+ /(?:EPERM, .+|Group identifier does not exist: nobody)$/
);
assert.throws(
() => { process.setuid('nobody'); },
- /^Error: (?:EPERM, .+|setuid user id does not exist)$/
+ /(?:EPERM, .+|User identifier does not exist: nobody)$/
);
return;
}
diff --git a/test/parallel/test-umask.js b/test/parallel/test-process-umask.js
index 1ac32cb701..869619f191 100644
--- a/test/parallel/test-umask.js
+++ b/test/parallel/test-process-umask.js
@@ -43,8 +43,17 @@ assert.strictEqual(old, process.umask());
assert.throws(() => {
process.umask({});
-}, /argument must be an integer or octal string/);
+}, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: 'The "mask" argument must be one of type number, string, or ' +
+ 'undefined. Received type object'
+});
-assert.throws(() => {
- process.umask('123x');
-}, /invalid octal string/);
+['123x', 'abc', '999'].forEach((value) => {
+ assert.throws(() => {
+ process.umask(value);
+ }, {
+ code: 'ERR_INVALID_ARG_VALUE',
+ message: `The argument 'mask' must be an octal string. Received '${value}'`
+ });
+});