v2.31.0-dev8 ec8c5b9

validation.go RawHistoryFolder

1package domain
2
3import (
4	"net"
5	"regexp"
6	"strings"
7)
8
9// hostnamePattern matches valid hostname characters (alphanumeric, hyphen, dot).
10// Used as a basic sanity check before detailed validation.
11// The actual validation uses net.ParseIP and explicit checks.
12var hostnamePattern = regexp.MustCompile(`^[a-zA-Z0-9.-]+$`)
13
14// IsValidRouteDomain validates that a domain is a public hostname suitable
15// for use as an application route. It rejects IP literals, localhost,
16// internal-only names, and authority strings containing ports.
17//
18// Valid: app.example.com, api.site.org, my-app.co.uk
19// Invalid: 192.168.1.1, localhost, myapp.local, example.com:8080
20func IsValidRouteDomain(domain string) bool {
21	// Reject empty
22	if domain == "" {
23		return false
24	}
25
26	// Basic character sanity check - only allow alphanumeric, hyphen, dot
27	// This also rejects paths, query strings, schemes (which contain :/ etc.)
28	if !hostnamePattern.MatchString(domain) {
29		return false
30	}
31
32	// Reject leading/trailing dots and consecutive dots
33	if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") ||
34		strings.Contains(domain, "..") {
35		return false
36	}
37
38	// Reject total hostname length > 253 (RFC 1035)
39	if len(domain) > 253 {
40		return false
41	}
42
43	// Must have at least one dot (domain + TLD minimum)
44	// This also rejects single-label names like "localhost", "myapp"
45	if !strings.Contains(domain, ".") {
46		return false
47	}
48
49	// Check if it looks like an IP address (IPv4 or IPv6)
50	// net.ParseIP accepts various formats including IPv6 brackets
51	if net.ParseIP(domain) != nil {
52		return false
53	}
54
55	// Check for internal-only TLDs
56	lowerDomain := strings.ToLower(domain)
57	if strings.HasSuffix(lowerDomain, ".local") ||
58		strings.HasSuffix(lowerDomain, ".internal") {
59		return false
60	}
61
62	// Check for localhost variants
63	if strings.HasPrefix(lowerDomain, "localhost.") {
64		return false
65	}
66	if strings.HasSuffix(lowerDomain, ".localhost") {
67		return false
68	}
69
70	// Validate each label conforms to RFC 1123
71	labels := strings.Split(domain, ".")
72	for _, label := range labels {
73		if !isValidLabel(label) {
74			return false
75		}
76	}
77
78	return true
79}
80
81// isValidLabel checks the per-label RFC 1123 length and hyphen rules.
82// Character allowlisting is enforced earlier by hostnamePattern.
83func isValidLabel(label string) bool {
84	if label == "" {
85		return false
86	}
87	if len(label) > 63 {
88		return false
89	}
90	if strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
91		return false
92	}
93	return true
94}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close