fix process-uid-gid.test.js

This commit is contained in:
Meghan Denny
2024-10-15 23:22:58 -07:00
parent 2d49f62034
commit 76147dad5a
4 changed files with 479 additions and 1 deletions

View File

@@ -45,6 +45,8 @@
#include <netdb.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <pwd.h>
#else
#include <uv.h>
#include <io.h>
@@ -2100,6 +2102,101 @@ JSC_DEFINE_HOST_FUNCTION(Process_functiongetgroups, (JSGlobalObject * globalObje
return JSValue::encode(groups);
}
static JSValue maybe_uid_by_name(JSC::ThrowScope& throwScope, JSGlobalObject* globalObject, JSValue value)
{
if (!value.isNumber() && !value.isString()) return JSValue::decode(Bun::ERR::INVALID_ARG_TYPE(throwScope, globalObject, "id"_s, "number or string"_s, value));
if (!value.isString()) return value;
auto str = value.getString(globalObject);
if (!str.is8Bit()) {
auto message = makeString("User identifier does not exist: "_s, str);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_UNKNOWN_CREDENTIAL, message));
return {};
}
auto name = (const char*)(str.span8().data());
struct passwd pwd;
struct passwd* pp = nullptr;
char buf[8192];
if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) {
return jsNumber(pp->pw_uid);
}
auto message = makeString("User identifier does not exist: "_s, str);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_UNKNOWN_CREDENTIAL, message));
return {};
}
JSC_DEFINE_HOST_FUNCTION(Process_functionsetuid, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto value = callFrame->argument(0);
value = maybe_uid_by_name(scope, globalObject, value);
RETURN_IF_EXCEPTION(scope, {});
Bun::V::validateInteger(scope, globalObject, value, jsString(vm, String("id"_s)), jsNumber(0), jsNumber(std::pow(2, 32)));
RETURN_IF_EXCEPTION(scope, {});
auto id = value.toUInt32(globalObject);
auto result = setuid(id);
if (result != 0) throwSystemError(scope, globalObject, "setuid"_s, errno);
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(jsNumber(result));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionseteuid, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto value = callFrame->argument(0);
value = maybe_uid_by_name(scope, globalObject, value);
RETURN_IF_EXCEPTION(scope, {});
Bun::V::validateInteger(scope, globalObject, value, jsString(vm, String("id"_s)), jsNumber(0), jsNumber(std::pow(2, 32)));
RETURN_IF_EXCEPTION(scope, {});
auto id = value.toUInt32(globalObject);
auto result = seteuid(id);
if (result != 0) throwSystemError(scope, globalObject, "seteuid"_s, errno);
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(jsNumber(result));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionsetegid, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto value = callFrame->argument(0);
value = maybe_uid_by_name(scope, globalObject, value);
RETURN_IF_EXCEPTION(scope, {});
Bun::V::validateInteger(scope, globalObject, value, jsString(vm, String("id"_s)), jsNumber(0), jsNumber(std::pow(2, 32)));
RETURN_IF_EXCEPTION(scope, {});
auto id = value.toUInt32(globalObject);
auto result = setegid(id);
if (result != 0) throwSystemError(scope, globalObject, "setegid"_s, errno);
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(jsNumber(result));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionsetgid, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto value = callFrame->argument(0);
value = maybe_uid_by_name(scope, globalObject, value);
RETURN_IF_EXCEPTION(scope, {});
Bun::V::validateInteger(scope, globalObject, value, jsString(vm, String("id"_s)), jsNumber(0), jsNumber(std::pow(2, 32)));
RETURN_IF_EXCEPTION(scope, {});
auto id = value.toUInt32(globalObject);
auto result = setgid(id);
if (result != 0) throwSystemError(scope, globalObject, "setgid"_s, errno);
RETURN_IF_EXCEPTION(scope, {});
return JSValue::encode(jsNumber(result));
}
JSC_DEFINE_HOST_FUNCTION(Process_functionsetgroups, (JSGlobalObject * globalObject, CallFrame* callFrame))
{
return JSValue::encode(jsNumber(0)); // TODO:
}
#endif
JSC_DEFINE_HOST_FUNCTION(Process_functionAssert, (JSGlobalObject * globalObject, CallFrame* callFrame))
@@ -3044,12 +3141,19 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu
_stopProfilerIdleNotifier Process_stubEmptyFunction Function 0
_tickCallback Process_stubEmptyFunction Function 0
_kill Process_functionReallyKill Function 2
#if !OS(WINDOWS)
getegid Process_functiongetegid Function 0
geteuid Process_functiongeteuid Function 0
getgid Process_functiongetgid Function 0
getgroups Process_functiongetgroups Function 0
getuid Process_functiongetuid Function 0
setegid Process_functionsetegid Function 1
seteuid Process_functionseteuid Function 1
setgid Process_functionsetgid Function 1
setgroups Process_functionsetgroups Function 1
setuid Process_functionsetuid Function 1
#endif
@end
*/

View File

@@ -46,6 +46,7 @@ export default [
["ERR_UNKNOWN_SIGNAL", TypeError, "TypeError"],
["ERR_SOCKET_BAD_PORT", RangeError, "RangeError"],
["ERR_IPC_ONE_PIPE", Error, "Error"],
["ERR_UNKNOWN_CREDENTIAL", Error, "Error"],
// Bun-specific
["ERR_FORMDATA_PARSE_ERROR", TypeError, "TypeError"],

View File

@@ -1,6 +1,260 @@
#include "root.h"
#include "helpers.h"
#include "BunClientData.h"
#include <string.h>
const char* errno_string(int errorno)
{
#define ERRNO_CASE(e) \
case e: \
return #e;
switch (errorno) {
#ifdef EACCES
ERRNO_CASE(EACCES);
#endif
#ifdef EADDRINUSE
ERRNO_CASE(EADDRINUSE);
#endif
#ifdef EADDRNOTAVAIL
ERRNO_CASE(EADDRNOTAVAIL);
#endif
#ifdef EAFNOSUPPORT
ERRNO_CASE(EAFNOSUPPORT);
#endif
#ifdef EAGAIN
ERRNO_CASE(EAGAIN);
#endif
#ifdef EWOULDBLOCK
#if EAGAIN != EWOULDBLOCK
ERRNO_CASE(EWOULDBLOCK);
#endif
#endif
#ifdef EALREADY
ERRNO_CASE(EALREADY);
#endif
#ifdef EBADF
ERRNO_CASE(EBADF);
#endif
#ifdef EBADMSG
ERRNO_CASE(EBADMSG);
#endif
#ifdef EBUSY
ERRNO_CASE(EBUSY);
#endif
#ifdef ECANCELED
ERRNO_CASE(ECANCELED);
#endif
#ifdef ECHILD
ERRNO_CASE(ECHILD);
#endif
#ifdef ECONNABORTED
ERRNO_CASE(ECONNABORTED);
#endif
#ifdef ECONNREFUSED
ERRNO_CASE(ECONNREFUSED);
#endif
#ifdef ECONNRESET
ERRNO_CASE(ECONNRESET);
#endif
#ifdef EDEADLK
ERRNO_CASE(EDEADLK);
#endif
#ifdef EDESTADDRREQ
ERRNO_CASE(EDESTADDRREQ);
#endif
#ifdef EDOM
ERRNO_CASE(EDOM);
#endif
#ifdef EDQUOT
ERRNO_CASE(EDQUOT);
#endif
#ifdef EEXIST
ERRNO_CASE(EEXIST);
#endif
#ifdef EFAULT
ERRNO_CASE(EFAULT);
#endif
#ifdef EFBIG
ERRNO_CASE(EFBIG);
#endif
#ifdef EHOSTUNREACH
ERRNO_CASE(EHOSTUNREACH);
#endif
#ifdef EIDRM
ERRNO_CASE(EIDRM);
#endif
#ifdef EILSEQ
ERRNO_CASE(EILSEQ);
#endif
#ifdef EINPROGRESS
ERRNO_CASE(EINPROGRESS);
#endif
#ifdef EINTR
ERRNO_CASE(EINTR);
#endif
#ifdef EINVAL
ERRNO_CASE(EINVAL);
#endif
#ifdef EIO
ERRNO_CASE(EIO);
#endif
#ifdef EISCONN
ERRNO_CASE(EISCONN);
#endif
#ifdef EISDIR
ERRNO_CASE(EISDIR);
#endif
#ifdef ELOOP
ERRNO_CASE(ELOOP);
#endif
#ifdef EMFILE
ERRNO_CASE(EMFILE);
#endif
#ifdef EMLINK
ERRNO_CASE(EMLINK);
#endif
#ifdef EMSGSIZE
ERRNO_CASE(EMSGSIZE);
#endif
#ifdef EMULTIHOP
ERRNO_CASE(EMULTIHOP);
#endif
#ifdef ENAMETOOLONG
ERRNO_CASE(ENAMETOOLONG);
#endif
#ifdef ENETDOWN
ERRNO_CASE(ENETDOWN);
#endif
#ifdef ENETRESET
ERRNO_CASE(ENETRESET);
#endif
#ifdef ENETUNREACH
ERRNO_CASE(ENETUNREACH);
#endif
#ifdef ENFILE
ERRNO_CASE(ENFILE);
#endif
#ifdef ENOBUFS
ERRNO_CASE(ENOBUFS);
#endif
#ifdef ENODATA
ERRNO_CASE(ENODATA);
#endif
#ifdef ENODEV
ERRNO_CASE(ENODEV);
#endif
#ifdef ENOENT
ERRNO_CASE(ENOENT);
#endif
#ifdef ENOEXEC
ERRNO_CASE(ENOEXEC);
#endif
#ifdef ENOLINK
ERRNO_CASE(ENOLINK);
#endif
#ifdef ENOLCK
#if ENOLINK != ENOLCK
ERRNO_CASE(ENOLCK);
#endif
#endif
#ifdef ENOMEM
ERRNO_CASE(ENOMEM);
#endif
#ifdef ENOMSG
ERRNO_CASE(ENOMSG);
#endif
#ifdef ENOPROTOOPT
ERRNO_CASE(ENOPROTOOPT);
#endif
#ifdef ENOSPC
ERRNO_CASE(ENOSPC);
#endif
#ifdef ENOSR
ERRNO_CASE(ENOSR);
#endif
#ifdef ENOSTR
ERRNO_CASE(ENOSTR);
#endif
#ifdef ENOSYS
ERRNO_CASE(ENOSYS);
#endif
#ifdef ENOTCONN
ERRNO_CASE(ENOTCONN);
#endif
#ifdef ENOTDIR
ERRNO_CASE(ENOTDIR);
#endif
#ifdef ENOTEMPTY
#if ENOTEMPTY != EEXIST
ERRNO_CASE(ENOTEMPTY);
#endif
#endif
#ifdef ENOTSOCK
ERRNO_CASE(ENOTSOCK);
#endif
#ifdef ENOTSUP
ERRNO_CASE(ENOTSUP);
#else
#ifdef EOPNOTSUPP
ERRNO_CASE(EOPNOTSUPP);
#endif
#endif
#ifdef ENOTTY
ERRNO_CASE(ENOTTY);
#endif
#ifdef ENXIO
ERRNO_CASE(ENXIO);
#endif
#ifdef EOVERFLOW
ERRNO_CASE(EOVERFLOW);
#endif
#ifdef EPERM
ERRNO_CASE(EPERM);
#endif
#ifdef EPIPE
ERRNO_CASE(EPIPE);
#endif
#ifdef EPROTO
ERRNO_CASE(EPROTO);
#endif
#ifdef EPROTONOSUPPORT
ERRNO_CASE(EPROTONOSUPPORT);
#endif
#ifdef EPROTOTYPE
ERRNO_CASE(EPROTOTYPE);
#endif
#ifdef ERANGE
ERRNO_CASE(ERANGE);
#endif
#ifdef EROFS
ERRNO_CASE(EROFS);
#endif
#ifdef ESPIPE
ERRNO_CASE(ESPIPE);
#endif
#ifdef ESRCH
ERRNO_CASE(ESRCH);
#endif
#ifdef ESTALE
ERRNO_CASE(ESTALE);
#endif
#ifdef ETIME
ERRNO_CASE(ETIME);
#endif
#ifdef ETIMEDOUT
ERRNO_CASE(ETIMEDOUT);
#endif
#ifdef ETXTBSY
ERRNO_CASE(ETXTBSY);
#endif
#ifdef EXDEV
ERRNO_CASE(EXDEV);
#endif
default:
return "";
}
}
JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral message, ASCIILiteral syscall, int err)
{
@@ -15,11 +269,12 @@ JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral message
JSC::JSValue createSystemError(JSC::JSGlobalObject* global, ASCIILiteral syscall, int err)
{
auto* instance = JSC::createError(global, makeString(String(syscall), "() failed"_s));
auto* instance = JSC::createError(global, makeString(String(syscall), "() failed: "_s, String::fromLatin1(strerror(err))));
auto& vm = global->vm();
auto& builtinNames = WebCore::builtinNames(vm);
instance->putDirect(vm, builtinNames.syscallPublicName(), jsString(vm, String(syscall)), 0);
instance->putDirect(vm, builtinNames.errnoPublicName(), JSC::jsNumber(err), 0);
instance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("SystemError"_s)), JSC::PropertyAttribute::DontEnum | 0);
instance->putDirect(vm, builtinNames.codePublicName(), jsString(vm, String::fromLatin1(errno_string(err))));
return instance;
}

View File

@@ -0,0 +1,118 @@
//#FILE: test-process-uid-gid.js
//#SHA1: fdd637ef2fcf3bcada2c86f574494e32e5c03780
//-----------------
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
"use strict";
const isWindows = process.platform === "win32";
const isMainThread = !process.env.NODE_WORKER_ID;
if (isWindows) {
test("uid/gid functions are POSIX only", () => {
// uid/gid functions are POSIX only.
expect(process.getuid).toBeUndefined();
expect(process.getgid).toBeUndefined();
expect(process.setuid).toBeUndefined();
expect(process.setgid).toBeUndefined();
});
} else if (isMainThread) {
test("setuid with invalid arguments", () => {
expect(() => process.setuid({})).toThrow(
expect.objectContaining({
code: "ERR_INVALID_ARG_TYPE",
message: expect.stringContaining('The "id" argument must be of type number or string'),
}),
);
expect(() => process.setuid("fhqwhgadshgnsdhjsdbkhsdabkfabkveyb")).toThrow(
expect.objectContaining({
code: "ERR_UNKNOWN_CREDENTIAL",
message: "User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb",
}),
);
});
test("edge cases for uid/gid functions", () => {
// Passing -0 shouldn't crash the process
// Refs: https://github.com/nodejs/node/issues/32750
// And neither should values exceeding 2 ** 31 - 1.
const ids = [-0, 2 ** 31, 2 ** 32 - 1];
const fns = [process.setuid, process.setuid, process.setgid, process.setegid];
for (const id of ids) {
for (const fn of fns) {
expect(() => {
try {
fn(id);
} catch {
// Continue regardless of error.
}
}).not.toThrow();
}
}
});
if (process.getuid() !== 0) {
test("non-root user permissions", () => {
// Should not throw.
expect(() => process.getgid()).not.toThrow();
expect(() => process.getuid()).not.toThrow();
expect(() => process.setgid("nobody")).toThrow(
expect.objectContaining({
syscall: "setgid",
code: "EPERM",
}),
);
expect(() => process.setuid("nobody")).toThrow(
expect.objectContaining({
syscall: "setuid",
code: "EPERM",
}),
);
});
} else {
test("root user permissions", async () => {
const oldgid = process.getgid();
try {
process.setgid("nobody");
} catch (err) {
if (err.code !== "ERR_UNKNOWN_CREDENTIAL") {
throw err;
}
process.setgid("nogroup");
}
const newgid = process.getgid();
expect(newgid).not.toBe(oldgid);
const olduid = process.getuid();
process.setuid("nobody");
const newuid = process.getuid();
expect(newuid).not.toBe(olduid);
});
}
}
//<#END_FILE: test-process-uid-gid.js