Harden IPv6 validation

This commit is contained in:
2026-04-17 10:38:23 +08:00
parent 014dc88a33
commit b5ae4a5668
2 changed files with 59 additions and 5 deletions

View File

@@ -247,13 +247,42 @@ validate_ipv4() {
done
}
_validate_ipv6_part_count() {
local part=${1-}
local IFS=':'
local -a fields=()
local field idx count=0
[[ -n ${part} ]] || {
printf '0\n'
return 0
}
[[ ${part} != :* ]] || return 1
[[ ${part} != *: ]] || return 1
[[ ${part} != *::* ]] || return 1
read -r -a fields <<<"${part}"
for idx in "${!fields[@]}"; do
field=${fields[idx]}
[[ -n ${field} ]] || return 1
if [[ ${field} == IPV4TAIL ]]; then
(( idx == ${#fields[@]} - 1 )) || return 1
((count += 2))
continue
fi
[[ ${field} =~ ^[0-9A-Fa-f]{1,4}$ ]] || return 1
((count++))
done
printf '%s\n' "${count}"
}
validate_ipv6() {
local ip=${1-}
local base scope
local base scope tail prefix left right left_count right_count total_count
[[ -n ${ip} ]] || return 1
[[ ${ip} != *.* ]] || return 1
[[ ${ip} == *:* ]] || return 1
[[ ${ip} != *:::* ]] || return 1
scope=''
base=${ip}
@@ -264,8 +293,29 @@ validate_ipv6() {
[[ ${scope} =~ ^[A-Za-z0-9_.:-]+$ ]] || return 1
fi
[[ ${base} =~ ^[0-9A-Fa-f:]+$ ]] || return 1
[[ ${base} =~ :: ]] || [[ ${base} =~ ^([0-9A-Fa-f]{1,4}:){1,7}[0-9A-Fa-f]{1,4}$ ]] || return 1
if [[ ${base} == *.* ]]; then
tail=${base##*:}
prefix=${base%:*}
[[ ${prefix} != "${base}" ]] || return 1
validate_ipv4 "${tail}" || return 1
base="${prefix}:IPV4TAIL"
fi
[[ ${base//IPV4TAIL/} =~ ^[0-9A-Fa-f:]+$ ]] || return 1
if [[ ${base} == *::* ]]; then
[[ ${base#*::} != *::* ]] || return 1
left=${base%%::*}
right=${base#*::}
left_count=$(_validate_ipv6_part_count "${left}") || return 1
right_count=$(_validate_ipv6_part_count "${right}") || return 1
total_count=$((left_count + right_count))
(( total_count < 8 )) || return 1
return 0
fi
total_count=$(_validate_ipv6_part_count "${base}") || return 1
(( total_count == 8 ))
}
validate_port() {

View File

@@ -26,7 +26,11 @@ assert_status 1 "$(status_of validate_ipv4 '')" 'validate_ipv4 should reject emp
assert_status 0 "$(status_of validate_ipv6 '::1')" 'validate_ipv6 should accept loopback'
assert_status 0 "$(status_of validate_ipv6 '2001:db8::1')" 'validate_ipv6 should accept compressed address'
assert_status 0 "$(status_of validate_ipv6 'fe80::1%eth0')" 'validate_ipv6 should accept scoped address'
assert_status 0 "$(status_of validate_ipv6 '::ffff:192.0.2.128')" 'validate_ipv6 should accept embedded ipv4 tail'
assert_status 1 "$(status_of validate_ipv6 ':::1')" 'validate_ipv6 should reject malformed address'
assert_status 1 "$(status_of validate_ipv6 '2001::1::1')" 'validate_ipv6 should reject multiple compression markers'
assert_status 1 "$(status_of validate_ipv6 '12345::1')" 'validate_ipv6 should reject oversized hextet'
assert_status 1 "$(status_of validate_ipv6 '2001:db8::1::')" 'validate_ipv6 should reject trailing double compression'
assert_status 1 "$(status_of validate_ipv6 '1.2.3.4')" 'validate_ipv6 should reject ipv4 literal'
assert_status 1 "$(status_of validate_ipv6 '')" 'validate_ipv6 should reject empty string'