/go/store/blobstore/git_blobstore.go: wip, normalize paths

This commit is contained in:
coffeegoddd☕️✨
2026-02-03 14:45:20 -08:00
parent 838a9f453b
commit 0b327d0f27
+58
View File
@@ -18,6 +18,7 @@ import (
"context"
"fmt"
"io"
"strings"
git "github.com/dolthub/dolt/go/store/blobstore/internal/git"
)
@@ -52,6 +53,10 @@ func (gbs *GitBlobstore) Path() string {
}
func (gbs *GitBlobstore) Exists(ctx context.Context, key string) (bool, error) {
key, err := normalizeGitTreePath(key)
if err != nil {
return false, err
}
commit, ok, err := git.TryResolveRefCommit(ctx, gbs.runner, gbs.ref)
if err != nil {
return false, err
@@ -70,6 +75,10 @@ func (gbs *GitBlobstore) Exists(ctx context.Context, key string) (bool, error) {
}
func (gbs *GitBlobstore) Get(ctx context.Context, key string, br BlobRange) (io.ReadCloser, uint64, string, error) {
key, err := normalizeGitTreePath(key)
if err != nil {
return nil, 0, "", err
}
commit, ok, err := git.TryResolveRefCommit(ctx, gbs.runner, gbs.ref)
if err != nil {
return nil, 0, "", err
@@ -139,14 +148,63 @@ func (l *limitReadCloser) Read(p []byte) (int, error) { return l.r.Read(p) }
func (l *limitReadCloser) Close() error { return l.c.Close() }
func (gbs *GitBlobstore) Put(ctx context.Context, key string, totalSize int64, reader io.Reader) (string, error) {
if _, err := normalizeGitTreePath(key); err != nil {
return "", err
}
return "", fmt.Errorf("%w: GitBlobstore.Put", git.ErrUnimplemented)
}
func (gbs *GitBlobstore) CheckAndPut(ctx context.Context, expectedVersion, key string, totalSize int64, reader io.Reader) (string, error) {
if _, err := normalizeGitTreePath(key); err != nil {
return "", err
}
return "", fmt.Errorf("%w: GitBlobstore.CheckAndPut", git.ErrUnimplemented)
}
func (gbs *GitBlobstore) Concatenate(ctx context.Context, key string, sources []string) (string, error) {
if _, err := normalizeGitTreePath(key); err != nil {
return "", err
}
for _, src := range sources {
if _, err := normalizeGitTreePath(src); err != nil {
return "", err
}
}
return "", fmt.Errorf("%w: GitBlobstore.Concatenate", git.ErrUnimplemented)
}
// normalizeGitTreePath normalizes and validates a blobstore key for use as a git tree path.
//
// Rules:
// - convert Windows-style separators: "\" -> "/"
// - disallow absolute paths (leading "/")
// - disallow empty segments and trailing "/"
// - disallow "." and ".." segments
// - disallow NUL bytes
func normalizeGitTreePath(key string) (string, error) {
if strings.ContainsRune(key, '\x00') {
return "", fmt.Errorf("invalid git blobstore key (NUL byte): %q", key)
}
key = strings.ReplaceAll(key, "\\", "/")
if key == "" {
return "", fmt.Errorf("invalid git blobstore key (empty)")
}
if strings.HasPrefix(key, "/") {
return "", fmt.Errorf("invalid git blobstore key (absolute path): %q", key)
}
parts := strings.Split(key, "/")
for _, p := range parts {
if p == "" {
return "", fmt.Errorf("invalid git blobstore key (empty path segment): %q", key)
}
if p == "." || p == ".." {
return "", fmt.Errorf("invalid git blobstore key (path traversal): %q", key)
}
if strings.ContainsRune(p, '\x00') {
return "", fmt.Errorf("invalid git blobstore key (NUL byte): %q", key)
}
}
return key, nil
}