Skip to content
Merged
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
22 changes: 19 additions & 3 deletions workspace-server/internal/handlers/container_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,19 @@ func (h *TemplatesHandler) copyFilesToContainer(ctx context.Context, containerNa

createdDirs := map[string]bool{}
for name, content := range files {
// CWE-22: reject absolute paths and path-traversal sequences
// before using the name in the tar header.
clean := filepath.Clean(name)
if filepath.IsAbs(clean) || strings.Contains(clean, "..") {
return fmt.Errorf("path traversal blocked: %s", name)
}
// Use the safe, cleaned name joined with destPath so the tar
// header Name is always a relative path inside destPath.
safeName := filepath.Join(destPath, clean)

// Create parent directories in tar (deduplicated)
dir := filepath.Dir(name)
if dir != "." && !createdDirs[dir] {
dir := filepath.Dir(safeName)
if dir != destPath && !createdDirs[dir] {
tw.WriteHeader(&tar.Header{
Typeflag: tar.TypeDir,
Name: dir + "/",
Expand All @@ -86,7 +96,7 @@ func (h *TemplatesHandler) copyFilesToContainer(ctx context.Context, containerNa

data := []byte(content)
header := &tar.Header{
Name: name,
Name: safeName,
Mode: 0644,
Size: int64(len(data)),
}
Expand Down Expand Up @@ -143,6 +153,12 @@ func (h *TemplatesHandler) deleteViaEphemeral(ctx context.Context, volumeName, f
return fmt.Errorf("docker not available")
}

// CWE-22: validate filePath before constructing the rm command so
// a path-traversal sequence cannot escape /configs.
if err := validateRelPath(filePath); err != nil {
return err
}

resp, err := h.docker.ContainerCreate(ctx, &container.Config{
Image: "alpine:latest",
Cmd: []string{"rm", "-rf", "/configs/" + filePath},
Expand Down
Loading