From b9dd58f4065de60a4385f253c72db45096d9be3e Mon Sep 17 00:00:00 2001 From: greymoth-jp Date: Mon, 29 Jun 2026 05:24:53 +0900 Subject: [PATCH 1/2] fix(fqdn): reject domain labels with a trailing hyphen fqdnRegexStringRFC1123 matched each label with [a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}, which allows a label to end in a hyphen (e.g. "foo-.example.com" or "example.com-"). The hostname validators were fixed for the same issue in #1565 (RFC 1123) and #1569 (RFC 952), but the fqdn pattern, whose comment says it should match hostnameRegexStringRFC1123, was left behind. Use the same label shape [a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? so a label must start and end with an alphanumeric. The non-numeric TLD, the optional trailing dot, and hyphens inside the last label (#1548) are preserved. --- regexes.go | 12 ++++++------ validator_test.go | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/regexes.go b/regexes.go index 20c03b84..06f9a40a 100644 --- a/regexes.go +++ b/regexes.go @@ -56,12 +56,12 @@ const ( latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$` - hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // https://tools.ietf.org/html/rfc952 - hostnameRegexStringRFC1123 = `^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 - fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9-]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.') - btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address - btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 - btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 + hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // https://tools.ietf.org/html/rfc952 + hostnameRegexStringRFC1123 = `^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123 + fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*?(\.[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.') + btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address + btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 + btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32 ethAddressRegexString = `^0x[0-9a-fA-F]{40}$` ethAddressUpperRegexString = `^0x[0-9A-F]{40}$` ethAddressLowerRegexString = `^0x[0-9a-f]{40}$` diff --git a/validator_test.go b/validator_test.go index 67d3f83a..9e005a9c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11108,6 +11108,10 @@ func TestFQDNValidation(t *testing.T) { {"24.example24.com", true}, {"test.24.example.com", true}, {"test-site-http.test-site", true}, + {"foo-.example.com", false}, + {"foo.bar-.com", false}, + {"example.com-", false}, + {"-foo.example.com", false}, {"test24.example24.com..", false}, {"example", false}, {"192.168.0.1", false}, From 47c584f12a070995bfaa726cd1c23672fc6685c7 Mon Sep 17 00:00:00 2001 From: greymoth-jp Date: Sat, 11 Jul 2026 19:11:29 +0900 Subject: [PATCH 2/2] test(fqdn): cover the 63-character max label length --- validator_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/validator_test.go b/validator_test.go index 9e005a9c..c07cff92 100644 --- a/validator_test.go +++ b/validator_test.go @@ -11111,6 +11111,8 @@ func TestFQDNValidation(t *testing.T) { {"foo-.example.com", false}, {"foo.bar-.com", false}, {"example.com-", false}, + {strings.Repeat("a", 63) + ".com", true}, + {strings.Repeat("a", 64) + ".com", false}, {"-foo.example.com", false}, {"test24.example24.com..", false}, {"example", false},