diff options
| author | 2026-03-07 15:08:14 +0800 | |
|---|---|---|
| committer | 2026-03-07 15:56:39 +0800 | |
| commit | 9d08dc994d51298e2d8e75d8ed4ee477312ec53a (patch) | |
| tree | 28c2a53174dd319fbd330132d446ef9ead8cb7ee /ref/refname/component.go | |
| parent | refstore: Remove Shorten for now (diff) | |
| signature | No signature | |
ref/refname: Add refname validation
Diffstat (limited to 'ref/refname/component.go')
| -rw-r--r-- | ref/refname/component.go | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/ref/refname/component.go b/ref/refname/component.go new file mode 100644 index 00000000..f5adba46 --- /dev/null +++ b/ref/refname/component.go @@ -0,0 +1,88 @@ +package refname + +import "strings" + +func checkRefnameComponent(name string, flags *int, sanitized *strings.Builder, fullName string) (int, error) { + var last byte + + componentStart := sanitizedLen(sanitized) + + for i := range len(name) { + ch := name[i] + disp := refnameDisposition(ch) + + if sanitized != nil && disp != 1 { + sanitized.WriteByte(ch) + } + + switch disp { + case 1: + goto out + case 2: + if last == '.' { + if sanitized != nil { + truncateBuilder(sanitized, sanitized.Len()-1) + } else { + return 0, &NameError{Name: fullName, Reason: "name contains '..'"} + } + } + case 3: + if last == '@' { + if sanitized != nil { + overwriteLastByte(sanitized, '-') + } else { + return 0, &NameError{Name: fullName, Reason: "name contains '@{'"} + } + } + case 4: + if sanitized != nil { + overwriteLastByte(sanitized, '-') + } else { + return 0, &NameError{Name: fullName, Reason: "name contains one forbidden character"} + } + case 5: + if *flags&refnameRefspecPattern == 0 { + if sanitized != nil { + overwriteLastByte(sanitized, '-') + } else { + return 0, &NameError{Name: fullName, Reason: "name contains '*'"} + } + } + + *flags &^= refnameRefspecPattern + } + + last = ch + } + +out: + componentLen := strings.IndexByte(name, '/') + + if componentLen < 0 { + componentLen = len(name) + } + + if componentLen == 0 { + return 0, nil + } + + if name[0] == '.' { + if sanitized != nil { + overwriteBuilderAt(sanitized, componentStart, '-') + } else { + return 0, &NameError{Name: fullName, Reason: "component starts with '.'"} + } + } + + if componentLen >= len(lockSuffix) && name[componentLen-len(lockSuffix):componentLen] == lockSuffix { + if sanitized == nil { + return 0, &NameError{Name: fullName, Reason: "component ends with .lock"} + } + + for strings.HasSuffix(sanitized.String(), lockSuffix) { + truncateBuilder(sanitized, sanitized.Len()-len(lockSuffix)) + } + } + + return componentLen, nil +} |
