Harden IPv6 validation
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user