fix: prevent directory traversal attack when writing request files
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -54,7 +53,10 @@ func (e *Docker) Exec(req Request) Execution {
|
||||
if e.cmd.Entry != "" {
|
||||
// write request files to the temp directory
|
||||
err = e.writeFiles(dir, req.Files)
|
||||
if err != nil {
|
||||
var argErr ArgumentError
|
||||
if errors.As(err, &argErr) {
|
||||
return Fail(req.ID, err)
|
||||
} else if err != nil {
|
||||
err = NewExecutionError("write files to temp dir", err)
|
||||
return Fail(req.ID, err)
|
||||
}
|
||||
@@ -171,7 +173,12 @@ func (e *Docker) writeFiles(dir string, files Files) error {
|
||||
if name == "" {
|
||||
name = e.cmd.Entry
|
||||
}
|
||||
path := filepath.Join(dir, name)
|
||||
var path string
|
||||
path, err = fileio.JoinDir(dir, name)
|
||||
if err != nil {
|
||||
err = NewArgumentError(fmt.Sprintf("files[%s]", name), err)
|
||||
return false
|
||||
}
|
||||
err = fileio.WriteFile(path, content, 0444)
|
||||
return err == nil
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -231,6 +232,29 @@ func TestDockerRun(t *testing.T) {
|
||||
t.Errorf("Stderr: unexpected value: %s", out.Stderr)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("directory traversal attack", func(t *testing.T) {
|
||||
mem.Clear()
|
||||
const fileName = "../../opt/codapi/codapi"
|
||||
engine := NewDocker(dockerCfg, "python", "run")
|
||||
req := Request{
|
||||
ID: "http_42",
|
||||
Sandbox: "python",
|
||||
Command: "run",
|
||||
Files: map[string]string{
|
||||
"": "print('hello world')",
|
||||
fileName: "hehe",
|
||||
},
|
||||
}
|
||||
out := engine.Exec(req)
|
||||
if out.OK {
|
||||
t.Error("OK: expected false")
|
||||
}
|
||||
want := fmt.Sprintf("files[%s]: invalid name", fileName)
|
||||
if out.Stderr != want {
|
||||
t.Errorf("Stderr: unexpected value: %s", out.Stderr)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDockerExec(t *testing.T) {
|
||||
|
||||
@@ -62,6 +62,25 @@ func (err ExecutionError) Unwrap() error {
|
||||
return err.inner
|
||||
}
|
||||
|
||||
// An ArgumentError is returned if code execution failed
|
||||
// due to the invalid value of the request agrument.
|
||||
type ArgumentError struct {
|
||||
name string
|
||||
reason error
|
||||
}
|
||||
|
||||
func NewArgumentError(name string, reason error) ArgumentError {
|
||||
return ArgumentError{name: name, reason: reason}
|
||||
}
|
||||
|
||||
func (err ArgumentError) Error() string {
|
||||
return err.name + ": " + err.reason.Error()
|
||||
}
|
||||
|
||||
func (err ArgumentError) Unwrap() error {
|
||||
return err.reason
|
||||
}
|
||||
|
||||
// Files are a collection of files to be executed by the engine.
|
||||
type Files map[string]string
|
||||
|
||||
|
||||
Reference in New Issue
Block a user