Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow Nodejs async fetch #121

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion internal/core/lib/nodejs/add_seccomp.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func InitSeccomp(uid int, gid int, enable_network bool) error {

allowed_syscalls := []int{}
allowed_not_kill_syscalls := []int{}
allowed_syscall_values := map[int]uint64{}

allowed_syscall := os.Getenv("ALLOWED_SYSCALLS")
if allowed_syscall != "" {
Expand All @@ -43,10 +44,11 @@ func InitSeccomp(uid int, gid int, enable_network bool) error {

if enable_network {
allowed_syscalls = append(allowed_syscalls, nodejs_syscall.ALLOW_NETWORK_SYSCALLS...)
allowed_syscall_values = nodejs_syscall.ALLOW_NETWORK_SYSCALL_VALUES
}
}

err = lib.Seccomp(allowed_syscalls, allowed_not_kill_syscalls)
err = lib.Seccomp(allowed_syscalls, allowed_not_kill_syscalls, allowed_syscall_values)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/core/lib/python/add_seccomp.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func InitSeccomp(uid int, gid int, enable_network bool) error {
}
}

err = lib.Seccomp(allowed_syscalls, allowed_not_kill_syscalls)
err = lib.Seccomp(allowed_syscalls, allowed_not_kill_syscalls, nil)
if err != nil {
return err
}
Expand Down
23 changes: 18 additions & 5 deletions internal/core/lib/seccomp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
sg "github.com/seccomp/libseccomp-golang"
)

func Seccomp(allowed_syscalls []int, allowed_not_kill_syscalls []int) error {
func Seccomp(allowed_syscalls []int, allowed_not_kill_syscalls []int, allowed_syscall_values map[int]uint64) error {
ctx, err := sg.NewFilter(sg.ActKillProcess)
if err != nil {
return err
Expand All @@ -23,12 +23,25 @@ func Seccomp(allowed_syscalls []int, allowed_not_kill_syscalls []int) error {
defer reader.Close()
defer writer.Close()

for _, syscall := range allowed_syscalls {
ctx.AddRule(sg.ScmpSyscall(syscall), sg.ActAllow)
for _, sc := range allowed_syscalls {
val, ok := allowed_syscall_values[sc]
if ok {
ctx.AddRuleConditional(sg.ScmpSyscall(sc), sg.ActAllow, []sg.ScmpCondition{
{Argument: 0, Op: sg.CompareEqual, Operand1: val},
})
} else {
ctx.AddRule(sg.ScmpSyscall(sc), sg.ActAllow)
}
}

for _, syscall := range allowed_not_kill_syscalls {
ctx.AddRule(sg.ScmpSyscall(syscall), sg.ActErrno)
for _, sc := range allowed_not_kill_syscalls {
switch sc {
case SYS_CLONE3:
// use ENOSYS for CLONE3, see: https://github.com/moby/moby/issues/42680
ctx.AddRule(sg.ScmpSyscall(sc), sg.ActErrno.SetReturnCode(int16(syscall.ENOSYS)))
default:
ctx.AddRule(sg.ScmpSyscall(sc), sg.ActErrno)
}
}

file := os.NewFile(uintptr(writer.Fd()), "pipe")
Expand Down
1 change: 1 addition & 0 deletions internal/core/lib/seccomp_syscall_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ package lib

const (
SYS_SECCOMP = 317
SYS_CLONE3 = 435
)
1 change: 1 addition & 0 deletions internal/core/lib/seccomp_syscall_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ import "syscall"

const (
SYS_SECCOMP = syscall.SYS_SECCOMP
SYS_CLONE3 = 435
)
31 changes: 28 additions & 3 deletions internal/static/nodejs_syscall/syscalls_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,24 @@

package nodejs_syscall

import "syscall"
import (
"syscall"

"golang.org/x/sys/unix"
)

const (
//334
SYS_RSEQ = 334
// 435
SYS_CLONE3 = 435

SYS_SENDMMSG = 307
SYS_GETRANDOM = 318
SYS_PKEY_ALLOC = 329
SYS_PKEY_MPROTECT = 330
SYS_PKEY_FREE = 331
SYS_STATX = 332
)

var ALLOW_SYSCALLS = []int{
Expand All @@ -30,23 +41,37 @@ var ALLOW_SYSCALLS = []int{
SYS_RSEQ,

syscall.SYS_SETUID, syscall.SYS_SETGID, syscall.SYS_GETTID,

syscall.SYS_CLOCK_GETTIME, syscall.SYS_GETTIMEOFDAY, syscall.SYS_NANOSLEEP,
syscall.SYS_TIME,

syscall.SYS_TGKILL,

syscall.SYS_READLINK,
syscall.SYS_DUP3,

syscall.SYS_PIPE2,
syscall.SYS_SET_TID_ADDRESS,
syscall.SYS_PREAD64, syscall.SYS_PWRITE64,

SYS_GETRANDOM,
syscall.SYS_EPOLL_CREATE1, syscall.SYS_EVENTFD2,
}

var ALLOW_ERROR_SYSCALLS = []int{
syscall.SYS_CLONE, SYS_CLONE3,
SYS_CLONE3, /* return ENOSYS for glibc */
}

var ALLOW_NETWORK_SYSCALLS = []int{
syscall.SYS_SOCKET, syscall.SYS_CONNECT, syscall.SYS_BIND, syscall.SYS_LISTEN, syscall.SYS_ACCEPT, syscall.SYS_SENDTO, syscall.SYS_RECVFROM,
syscall.SYS_GETSOCKNAME, syscall.SYS_RECVMSG, syscall.SYS_GETPEERNAME, syscall.SYS_SETSOCKOPT, syscall.SYS_PPOLL, syscall.SYS_UNAME,
syscall.SYS_SENDMSG, syscall.SYS_GETSOCKOPT,
syscall.SYS_FCNTL, syscall.SYS_FSTATFS,
SYS_SENDMMSG, syscall.SYS_POLL,
SYS_PKEY_ALLOC, SYS_PKEY_MPROTECT, SYS_PKEY_FREE, SYS_STATX,
syscall.SYS_CLONE,
}

var ALLOW_NETWORK_SYSCALL_VALUES = map[int]uint64{
// allow clone for nodejs networking
syscall.SYS_CLONE: unix.CLONE_VM | unix.CLONE_FS | unix.CLONE_FILES | unix.CLONE_SIGHAND | unix.CLONE_THREAD | unix.CLONE_SYSVSEM | unix.CLONE_SETTLS | unix.CLONE_PARENT_SETTID | unix.CLONE_CHILD_CLEARTID,
}
2 changes: 2 additions & 0 deletions internal/static/nodejs_syscall/syscalls_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ var ALLOW_NETWORK_SYSCALLS = []int{
syscall.SYS_FSTATAT, syscall.SYS_LSEEK,
syscall.SYS_FSTATFS,
}

var ALLOW_NETWORK_SYSCALL_VALUES = map[int]uint64{}
78 changes: 77 additions & 1 deletion tests/integration_tests/nodejs_feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ console.log(JSON.stringify({"hello": "world"}));
EnableNetwork: true,
})
if resp.Code != 0 {
t.Error(resp)
t.Fatal(resp)
}

if resp.Data.(*service.RunCodeResponse).Stderr != "" {
Expand All @@ -79,3 +79,79 @@ console.log(JSON.stringify({"hello": "world"}));
}
})
}

func TestNodejsAsyncTemplate(t *testing.T) {
const code = `// declare main function
function main({a}) {
return {b: a}
}

async function wrap() {
// decode and prepare input object
var inputs_obj = JSON.parse(Buffer.from('eyJhIjoiYSJ9', 'base64').toString('utf-8'))

// execute main function
var output_obj = await main(inputs_obj)

// convert output to json and print
var output_json = JSON.stringify(output_obj)
var result = ` + "`<<RESULT>>${output_json}<<RESULT>>`" + `
console.log(result)
}
wrap()
`

runMultipleTestings(t, 30, func(t *testing.T) {
resp := service.RunNodeJsCode(code, "", &types.RunnerOptions{
EnableNetwork: false,
})
if resp.Code != 0 {
t.Error(resp)
}
if resp.Data.(*service.RunCodeResponse).Stderr != "" {
t.Fatalf("unexpected error: %s\n", resp.Data.(*service.RunCodeResponse).Stderr)
}
if !strings.Contains(resp.Data.(*service.RunCodeResponse).Stdout, `{"b":"a"}`) {
t.Fatalf("unexpected output: %s\n", resp.Data.(*service.RunCodeResponse).Stdout)
}
})
}

func TestNodejsAsyncFetch(t *testing.T) {
// Test case for json
runMultipleTestings(t, 30, func(t *testing.T) {
const code = `// declare main function
async function main({a}) {
return {b: await (await fetch("https://www.bilibili.com")).text()}
}

// decode and prepare input object
var inputs_obj = JSON.parse(Buffer.from('eyJhIjoiYSJ9', 'base64').toString('utf-8'))

async function wrap() {
// execute main function
var output_obj = await main(inputs_obj)

// convert output to json and print
var output_json = JSON.stringify(output_obj)
var result = ` + "`<<RESULT>>${output_json}<<RESULT>>`" + `
console.log(result)
}
wrap()
`
resp := service.RunNodeJsCode(code, "", &types.RunnerOptions{
EnableNetwork: true,
})
if resp.Code != 0 {
t.Error(resp)
}

if resp.Data.(*service.RunCodeResponse).Stderr != "" {
t.Fatalf("unexpected error: %s\n", resp.Data.(*service.RunCodeResponse).Stderr)
}

if !strings.Contains(resp.Data.(*service.RunCodeResponse).Stdout, `bilibili`) {
t.Fatalf("unexpected output: %s\n", resp.Data.(*service.RunCodeResponse).Stdout)
}
})
}