From dc218212be17db600c7019b0f9409ab178a25468 Mon Sep 17 00:00:00 2001 From: "molecule-ai[bot]" <276602405+molecule-ai[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 06:32:11 +0000 Subject: [PATCH] fix(security): CWE-22 path traversal in copyFilesToContainer and deleteViaEphemeral CWE-22 fix: - copyFilesToContainer: validate with filepath.Clean + IsAbs + strings.Contains(clean, '..'), use safeName for tar header - deleteViaEphemeral: call validateRelPath(filePath) before constructing rm command Fixes #1272 --- .../internal/handlers/container_files.go | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/workspace-server/internal/handlers/container_files.go b/workspace-server/internal/handlers/container_files.go index 838e09ee..5d920a47 100644 --- a/workspace-server/internal/handlers/container_files.go +++ b/workspace-server/internal/handlers/container_files.go @@ -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 + "/", @@ -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)), } @@ -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},