~bigbes/sr-ht-dolt

ref: 2c8903fd8ef6f2e3843f9373be268cf9136ba9e2 sr-ht-dolt/core/names.go -rw-r--r-- 2.3 KiB
2c8903fd — Eugene Blikh browse: report an unparseable start hash as a missing ref 5 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package core

import (
	"fmt"
	"regexp"
	"strings"
)

// MaxNameLen is the maximum length of a database name. Names double as SQL
// database identifiers, so they are kept short and conservative.
const MaxNameLen = 64

// nameRe matches a valid database (or owner) name: an alphanumeric first and
// last character, with alphanumerics, hyphens and underscores in between. It
// structurally forbids "." and ".." (no dots at all) and leading/trailing
// separators.
var nameRe = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$`)

// ValidateName reports whether name is an acceptable database name. It rejects
// the empty string, names longer than MaxNameLen, the traversal names "." and
// ".." explicitly, and anything not matching nameRe.
func ValidateName(name string) error {
	if name == "" {
		return fmt.Errorf("name must not be empty")
	}
	if name == "." || name == ".." {
		return fmt.Errorf("name %q is not allowed", name)
	}
	if len(name) > MaxNameLen {
		return fmt.Errorf("name is too long (%d > %d)", len(name), MaxNameLen)
	}
	if !nameRe.MatchString(name) {
		return fmt.Errorf("name %q must match %s", name, nameRe.String())
	}
	return nil
}

// ParseRepoPath splits a repository path into its owner and database segments.
// It accepts "~user/db", "user/db", and leading/trailing slashes. It rejects
// empty segments, ".."/"." traversal segments, and any path that does not have
// exactly two segments. The returned owner never carries a leading "~".
//
// ParseRepoPath validates structure only; owner and db are not name-validated
// here (callers that touch disk or SQL must additionally run ValidateName).
func ParseRepoPath(path string) (owner, db string, err error) {
	trimmed := strings.Trim(path, "/")
	if trimmed == "" {
		return "", "", fmt.Errorf("empty repo path")
	}
	segs := strings.Split(trimmed, "/")
	if len(segs) != 2 {
		return "", "", fmt.Errorf("repo path %q must have exactly 2 segments, got %d", path, len(segs))
	}
	for _, s := range segs {
		if s == "" {
			return "", "", fmt.Errorf("repo path %q has an empty segment", path)
		}
		if s == "." || s == ".." {
			return "", "", fmt.Errorf("repo path %q contains a traversal segment %q", path, s)
		}
	}
	owner = strings.TrimPrefix(segs[0], "~")
	db = segs[1]
	if owner == "" {
		return "", "", fmt.Errorf("repo path %q has an empty owner", path)
	}
	return owner, db, nil
}