From b6f942651b6bd2564f247cf3fd9ea699378741a8 Mon Sep 17 00:00:00 2001 From: ra2y Date: Fri, 24 Apr 2026 03:50:37 -0700 Subject: [PATCH 1/2] implement sandbox runner --- internal/execution/sandbox.go | 107 ++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 internal/execution/sandbox.go diff --git a/internal/execution/sandbox.go b/internal/execution/sandbox.go new file mode 100644 index 0000000..affed58 --- /dev/null +++ b/internal/execution/sandbox.go @@ -0,0 +1,107 @@ +package execution + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "time" +) + +type Result struct { + Stdout string `json:"stdout"` + Stderr string `json:"stderr"` + Code int `json:"code"` +} + +func Run(ctx context.Context, language string, sourceCode string, stdin string) (*Result, error) { + // create a temp dir + tempDir, err := os.MkdirTemp("", "codehere") + if err != nil { + return nil, err + } + // makes sure temp directory and contents are deleted when run is done + defer os.RemoveAll(tempDir) + + filePath := filepath.Join(tempDir, "main.py") + + // write source code to file named main.py + err = os.WriteFile(filePath, []byte(sourceCode), 0644) + if err != nil { + return nil, err + } + + // get executiontimeout variable to time + timeoutStr := os.Getenv("EXECUTION_TIMEOUT") + if timeoutStr == "" { + timeoutStr = "10s" + } + timeout, err := time.ParseDuration(timeoutStr) + if err != nil { + return nil, err + } + + // enforce hard timeout + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // get memory limit from env variable + memoryLimit := os.Getenv("SANDBOX_MEMORY_LIMIT") + if memoryLimit == "" { + memoryLimit = "256m" + } + + args := []string{ + "run", "--rm", "-i", + "--network=none", + "--read-only", + "--memory=" + memoryLimit, + "--memory-swap=" + memoryLimit, + "--cpus=0.5", + "--pids-limit=64", + "--tmpfs", "/tmp:rw,noexec,nosuid,size=64m", + "-v", fmt.Sprintf("%s:/code:ro", tempDir), + "python:3.12-slim", + "sh", "-c", "python /code/main.py", + } + + // run da command and pipe stdin to container i think + cmd := exec.CommandContext(ctx, "docker", args...) + cmd.Stdin = strings.NewReader(stdin) + + //capture studout/stderr from the container + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err = cmd.Run() + + // do you know you have 10 seconds.... + // 10? 10, 10, yes.... + if ctx.Err() == context.DeadlineExceeded { + return &Result{ + Stdout: stdout.String(), + Stderr: "execution timed out (" + timeoutStr + "s limit)", + Code: -1, + }, nil + } + + exitCode := 0 + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + exitCode = exitErr.ExitCode() + } else { + return nil, err + } + } + // WHEN THE GO FILE FINALLY TURNS GREEN 🥹🥹🥹 + return &Result{ + Stdout: stdout.String(), + Stderr: stderr.String(), + Code: exitCode, + }, nil +} From 5a658900a0e06d462e40842e78b4bcb1f575d70f Mon Sep 17 00:00:00 2001 From: ra2y Date: Tue, 28 Apr 2026 01:37:19 -0700 Subject: [PATCH 2/2] actually get execution time and use sandbox image --- internal/execution/sandbox.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/internal/execution/sandbox.go b/internal/execution/sandbox.go index affed58..7896243 100644 --- a/internal/execution/sandbox.go +++ b/internal/execution/sandbox.go @@ -12,9 +12,10 @@ import ( ) type Result struct { - Stdout string `json:"stdout"` - Stderr string `json:"stderr"` - Code int `json:"code"` + Stdout string `json:"stdout"` + Stderr string `json:"stderr"` + Code int `json:"code"` + ExecutionMS int64 `json:"execution_ms"` } func Run(ctx context.Context, language string, sourceCode string, stdin string) (*Result, error) { @@ -64,8 +65,7 @@ func Run(ctx context.Context, language string, sourceCode string, stdin string) "--pids-limit=64", "--tmpfs", "/tmp:rw,noexec,nosuid,size=64m", "-v", fmt.Sprintf("%s:/code:ro", tempDir), - "python:3.12-slim", - "sh", "-c", "python /code/main.py", + "codesce-sandbox-python:latest", } // run da command and pipe stdin to container i think @@ -78,15 +78,18 @@ func Run(ctx context.Context, language string, sourceCode string, stdin string) cmd.Stdout = &stdout cmd.Stderr = &stderr + start := time.Now() err = cmd.Run() + duration := time.Since(start).Milliseconds() // do you know you have 10 seconds.... // 10? 10, 10, yes.... if ctx.Err() == context.DeadlineExceeded { return &Result{ - Stdout: stdout.String(), - Stderr: "execution timed out (" + timeoutStr + "s limit)", - Code: -1, + Stdout: stdout.String(), + Stderr: "execution timed out (" + timeoutStr + " limit)", + Code: -1, + ExecutionMS: duration, }, nil } @@ -100,8 +103,9 @@ func Run(ctx context.Context, language string, sourceCode string, stdin string) } // WHEN THE GO FILE FINALLY TURNS GREEN 🥹🥹🥹 return &Result{ - Stdout: stdout.String(), - Stderr: stderr.String(), - Code: exitCode, + Stdout: stdout.String(), + Stderr: stderr.String(), + Code: exitCode, + ExecutionMS: duration, }, nil }