main bac21c6

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 CanonicalRouteDomain(domainName string) (string, bool) {
21	canonical := strings.ToLower(strings.TrimSpace(domainName))
22	if !IsValidRouteDomain(canonical) {
23		return "", false
24	}
25	return canonical, true
26}
27
28func IsValidRouteDomain(domain string) bool {
29	// Reject empty
30	if domain == "" {
31		return false
32	}
33
34	// Basic character sanity check - only allow alphanumeric, hyphen, dot
35	// This also rejects paths, query strings, schemes (which contain :/ etc.)
36	if !hostnamePattern.MatchString(domain) {
37		return false
38	}
39
40	// Reject leading/trailing dots and consecutive dots
41	if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") ||
42		strings.Contains(domain, "..") {
43		return false
44	}
45
46	// Reject total hostname length > 253 (RFC 1035)
47	if len(domain) > 253 {
48		return false
49	}
50
51	// Must have at least one dot (domain + TLD minimum)
52	// This also rejects single-label names like "localhost", "myapp"
53	if !strings.Contains(domain, ".") {
54		return false
55	}
56
57	// Check if it looks like an IP address (IPv4 or IPv6)
58	// net.ParseIP accepts various formats including IPv6 brackets
59	if net.ParseIP(domain) != nil {
60		return false
61	}
62
63	// Check for internal-only TLDs
64	lowerDomain := strings.ToLower(domain)
65	if strings.HasSuffix(lowerDomain, ".local") ||
66		strings.HasSuffix(lowerDomain, ".internal") {
67		return false
68	}
69
70	// Check for localhost variants
71	if strings.HasPrefix(lowerDomain, "localhost.") {
72		return false
73	}
74	if strings.HasSuffix(lowerDomain, ".localhost") {
75		return false
76	}
77
78	// Validate each label conforms to RFC 1123
79	labels := strings.Split(domain, ".")
80	for _, label := range labels {
81		if !isValidLabel(label) {
82			return false
83		}
84	}
85
86	return true
87}
88
89// isValidLabel checks the per-label RFC 1123 length and hyphen rules.
90// Character allowlisting is enforced earlier by hostnamePattern.
91func isValidLabel(label string) bool {
92	if label == "" {
93		return false
94	}
95	if len(label) > 63 {
96		return false
97	}
98	if strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
99		return false
100	}
101	return true
102}

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