diff --git a/gcp/indexer/shared/shared.go b/gcp/indexer/shared/shared.go index 9c677135678..0fc312aeba2 100644 --- a/gcp/indexer/shared/shared.go +++ b/gcp/indexer/shared/shared.go @@ -19,9 +19,11 @@ package shared import ( "archive/tar" "context" + "fmt" "io" "os" "path/filepath" + "strings" "cloud.google.com/go/storage" ) @@ -45,6 +47,7 @@ func CopyFromBucket(ctx context.Context, bucketHdl *storage.BucketHandle, name s if err != nil { return "", err } + defer r.Close() tarRdr := tar.NewReader(r) for { hdr, err := tarRdr.Next() @@ -55,11 +58,32 @@ func CopyFromBucket(ctx context.Context, bucketHdl *storage.BucketHandle, name s return "", err } + name := filepath.Clean(hdr.Name) + if filepath.IsAbs(name) || name == ".." || strings.HasPrefix(name, ".."+string(os.PathSeparator)) { + return "", fmt.Errorf("invalid tar path: %q", hdr.Name) + } + path := filepath.Clean(filepath.Join(tmpDir, name)) + if path != tmpDir && !strings.HasPrefix(path, tmpDir+string(os.PathSeparator)) { + return "", fmt.Errorf("invalid tar path: %q", hdr.Name) + } + + switch hdr.Typeflag { + case tar.TypeDir: + if err := os.MkdirAll(path, 0760); err != nil { + return "", err + } + continue + case tar.TypeReg, tar.TypeRegA: + case tar.TypeSymlink, tar.TypeLink: + return "", fmt.Errorf("unsupported tar entry type: %q", hdr.Name) + default: + return "", fmt.Errorf("unsupported tar entry type: %q", hdr.Name) + } + buf, err := io.ReadAll(tarRdr) if err != nil { return "", err } - path := filepath.Clean(filepath.Join(tmpDir, hdr.Name)) if err := os.MkdirAll(filepath.Dir(path), 0760); err != nil { return "", err } diff --git a/go/cmd/exporter/writer.go b/go/cmd/exporter/writer.go index 363358a80b9..183c938fe18 100644 --- a/go/cmd/exporter/writer.go +++ b/go/cmd/exporter/writer.go @@ -5,6 +5,7 @@ import ( "log/slog" "os" "path/filepath" + "strings" "sync" "github.com/google/osv.dev/go/logger" @@ -42,16 +43,44 @@ func writer(ctx context.Context, cancel context.CancelFunc, inCh <-chan writeMsg break } } else { + cleanPath := filepath.Clean(msg.path) + if filepath.IsAbs(cleanPath) || cleanPath == ".." || strings.HasPrefix(cleanPath, ".."+string(filepath.Separator)) { + logger.Error("invalid file path", slog.String("path", msg.path)) + cancel() + + break + } + basePath, err := filepath.Abs(pathPrefix) + if err != nil { + logger.Error("failed to get absolute path", slog.String("path", pathPrefix), slog.Any("err", err)) + cancel() + + break + } + localPath, err := filepath.Abs(filepath.Join(basePath, cleanPath)) + if err != nil { + logger.Error("failed to get absolute path", slog.String("path", cleanPath), slog.Any("err", err)) + cancel() + + break + } + relPath, err := filepath.Rel(basePath, localPath) + if err != nil || relPath == ".." || strings.HasPrefix(relPath, ".."+string(filepath.Separator)) { + logger.Error("invalid file path", slog.String("path", msg.path), slog.Any("err", err)) + cancel() + + break + } // Write locally. - dir := filepath.Dir(path) + dir := filepath.Dir(localPath) if err := os.MkdirAll(dir, 0755); err != nil { logger.Error("failed to create directories", slog.String("dir", dir), slog.Any("err", err)) cancel() break } - if err := os.WriteFile(path, msg.data, 0600); err != nil { - logger.Error("failed to write file", slog.String("path", path), slog.Any("err", err)) + if err := os.WriteFile(localPath, msg.data, 0600); err != nil { + logger.Error("failed to write file", slog.String("path", localPath), slog.Any("err", err)) cancel() break