coverage~bigbes/sr-ht-dolt3523280ccore/names.go

Coverage
96.0% 24/25 statements
Δ
+0.0
Blob
de665a8
Uncovered L56-L57
1 package core
2
3 import (
4 "fmt"
5 "regexp"
6 "strings"
7 )
8
9 // MaxNameLen is the maximum length of a database name. Names double as SQL
10 // database identifiers, so they are kept short and conservative.
11 const MaxNameLen = 64
12
13 // nameRe matches a valid database (or owner) name: an alphanumeric first and
14 // last character, with alphanumerics, hyphens and underscores in between. It
15 // structurally forbids "." and ".." (no dots at all) and leading/trailing
16 // separators.
17 var nameRe = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$`)
18
19 // ValidateName reports whether name is an acceptable database name. It rejects
20 // the empty string, names longer than MaxNameLen, the traversal names "." and
21 // ".." explicitly, and anything not matching nameRe.
22 21 func ValidateName(name string) error {
23 21 if name == "" {
24 1 return fmt.Errorf("name must not be empty")
25 1 }
26 20 if name == "." || name == ".." {
27 2 return fmt.Errorf("name %q is not allowed", name)
28 2 }
29 18 if len(name) > MaxNameLen {
30 1 return fmt.Errorf("name is too long (%d > %d)", len(name), MaxNameLen)
31 1 }
32 17 if !nameRe.MatchString(name) {
33 9 return fmt.Errorf("name %q must match %s", name, nameRe.String())
34 9 }
35 8 return nil
36 }
37
38 // ParseRepoPath splits a repository path into its owner and database segments.
39 // It accepts "~user/db", "user/db", and leading/trailing slashes. It rejects
40 // empty segments, ".."/"." traversal segments, and any path that does not have
41 // exactly two segments. The returned owner never carries a leading "~".
42 //
43 // ParseRepoPath validates structure only; owner and db are not name-validated
44 // here (callers that touch disk or SQL must additionally run ValidateName).
45 15 func ParseRepoPath(path string) (owner, db string, err error) {
46 15 trimmed := strings.Trim(path, "/")
47 15 if trimmed == "" {
48 2 return "", "", fmt.Errorf("empty repo path")
49 2 }
50 13 segs := strings.Split(trimmed, "/")
51 13 if len(segs) != 2 {
52 3 return "", "", fmt.Errorf("repo path %q must have exactly 2 segments, got %d", path, len(segs))
53 3 }
54 19 for _, s := range segs {
55 19 if s == "" {
56 0 return "", "", fmt.Errorf("repo path %q has an empty segment", path)
57 0 }
58 19 if s == "." || s == ".." {
59 3 return "", "", fmt.Errorf("repo path %q contains a traversal segment %q", path, s)
60 3 }
61 }
62 7 owner = strings.TrimPrefix(segs[0], "~")
63 7 db = segs[1]
64 7 if owner == "" {
65 1 return "", "", fmt.Errorf("repo path %q has an empty owner", path)
66 1 }
67 6 return owner, db, nil
68 }