K-114: address cross-review findings
CI / Shell-Lint (bash -n, source-check, Validierungs-Tests) (pull_request) Has been cancelled
CI / Shell-Lint (bash -n, source-check, Validierungs-Tests) (pull_request) Has been cancelled
- CTID prompt re-prompts on invalid interactive input (was: abort)
- env-provided NAMESERVER is validated when non-empty ('' stays inherit)
- prompt_validated handles EOF (no infinite loop, clean abort under -e)
- 10# base forcing in vlan/cidr/ipv4 arithmetic (leading zeros are not
octal errors); is_clean_ascii rejects embedded newline/tab explicitly
(command substitution strips trailing newlines); is_ipv4_list checks
the whole string before word splitting
- nameref guard against reserved variable names in prompt_validated/
require_valid; source-check pattern documented as the repo contract
- 8 new test cases (41 total)
This commit is contained in:
+43
-10
@@ -116,20 +116,25 @@ sanitize_input() {
|
||||
# bytes). Byte-exact via tr: delete all printable ASCII — anything left
|
||||
# over is junk.
|
||||
is_clean_ascii() {
|
||||
# Newline/Tab zuerst explizit ablehnen — $(…) strippt trailing newlines,
|
||||
# die der tr-Pfad sonst übersehen würde (Review-Finding K-114).
|
||||
[[ "$1" == *$'\n'* || "$1" == *$'\t'* ]] && return 1
|
||||
local leftover
|
||||
leftover="$(printf '%s' "$1" | LC_ALL=C tr -d '\40-\176')"
|
||||
[[ -z "$leftover" ]]
|
||||
}
|
||||
|
||||
is_uint() { is_clean_ascii "$1" && [[ "$1" =~ ^[0-9]+$ ]]; }
|
||||
is_vlan_tag() { is_uint "$1" && (( $1 >= 1 && $1 <= 4094 )); }
|
||||
# 10#: führende Nullen nicht als Oktal werten ("08" wäre sonst ein
|
||||
# Arithmetik-Fehler statt einer sauberen Ablehnung/Annahme).
|
||||
is_vlan_tag() { is_uint "$1" && (( 10#$1 >= 1 && 10#$1 <= 4094 )); }
|
||||
is_token() { is_clean_ascii "$1" && [[ "$1" =~ ^[A-Za-z0-9._-]+$ ]]; }
|
||||
is_hostname() { is_clean_ascii "$1" && [[ "$1" =~ ^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?$ ]]; }
|
||||
|
||||
is_ipv4() {
|
||||
is_clean_ascii "$1" && [[ "$1" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]] || return 1
|
||||
local o
|
||||
for o in "${BASH_REMATCH[@]:1:4}"; do (( o <= 255 )) || return 1; done
|
||||
for o in "${BASH_REMATCH[@]:1:4}"; do (( 10#$o <= 255 )) || return 1; done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -137,13 +142,15 @@ is_cidr() {
|
||||
[[ "$1" =~ ^([0-9.]+)/([0-9]{1,2})$ ]] || return 1
|
||||
# BASH_REMATCH retten — is_ipv4 nutzt selbst =~ und überschreibt es.
|
||||
local _ip="${BASH_REMATCH[1]}" _prefix="${BASH_REMATCH[2]}"
|
||||
is_ipv4 "$_ip" && (( _prefix >= 1 && _prefix <= 32 ))
|
||||
is_ipv4 "$_ip" && (( 10#$_prefix >= 1 && 10#$_prefix <= 32 ))
|
||||
}
|
||||
|
||||
is_ipcfg() { [[ "$1" == "dhcp" ]] || is_cidr "$1"; }
|
||||
|
||||
# Space/comma-separated list of IPv4s (DNS prompt).
|
||||
# Space/comma-separated list of IPv4s (DNS prompt). Gesamtstring zuerst
|
||||
# prüfen — die Wort-Splittung würde eingebettete Newlines sonst verstecken.
|
||||
is_ipv4_list() {
|
||||
is_clean_ascii "$1" || return 1
|
||||
local item
|
||||
for item in ${1//,/ }; do is_ipv4 "$item" || return 1; done
|
||||
[[ -n "$1" ]]
|
||||
@@ -154,11 +161,18 @@ is_ipv4_list() {
|
||||
# empty input and re-prompts until the validator passes. allow_empty=yes lets
|
||||
# an empty value through (e.g. "no VLAN").
|
||||
prompt_validated() {
|
||||
# Schutz vor zirkulärem nameref (Review-Finding): interne Namen tabu.
|
||||
[[ "$1" == _pv_* || "$1" == _rv_* ]] && { msg_err "prompt_validated: reserved variable name '$1'"; return 2; }
|
||||
local -n _pv_ref="$1"
|
||||
local _pv_prompt="$2" _pv_validator="$3" _pv_default="${4-}" _pv_allow_empty="${5:-no}"
|
||||
local _pv_value
|
||||
while true; do
|
||||
read -rp "$_pv_prompt" _pv_value
|
||||
if ! read -rp "$_pv_prompt" _pv_value; then
|
||||
# EOF (kein TTY / stdin erschöpft): kein Endlos-Loop, sauber raus —
|
||||
# unter set -e bricht der Caller damit kontrolliert ab.
|
||||
msg_err "No input available for prompt: ${_pv_prompt%% *}"
|
||||
return 1
|
||||
fi
|
||||
_pv_value="$(sanitize_input "$_pv_value")"
|
||||
if [[ -z "$_pv_value" && -n "$_pv_default" ]]; then
|
||||
_pv_value="$_pv_default"
|
||||
@@ -182,6 +196,7 @@ prompt_validated() {
|
||||
|
||||
# Validate an env-provided value (non-interactive: abort instead of re-prompt).
|
||||
require_valid() {
|
||||
[[ "$1" == _pv_* || "$1" == _rv_* ]] && { msg_err "require_valid: reserved variable name '$1'"; return 2; }
|
||||
local -n _rv_ref="$1"
|
||||
local _rv_validator="$2" _rv_label="$3"
|
||||
_rv_ref="$(sanitize_input "$_rv_ref")"
|
||||
@@ -195,11 +210,24 @@ require_valid() {
|
||||
# via env skips the prompt (= "no VLAN" / "inherit DNS from host").
|
||||
prompt_lxc_config() {
|
||||
if [[ -z "${CTID:-}" ]]; then
|
||||
read -rp "Container ID [auto]: " CTID
|
||||
CTID="$(sanitize_input "$CTID")"
|
||||
[[ -z "${CTID:-}" ]] && CTID=$(pvesh get /cluster/nextid)
|
||||
# Eigener Loop statt prompt_validated: leer = auto (pvesh nextid),
|
||||
# ungültig = Re-Prompt (Review-Finding: vorher Abbruch statt Re-Prompt).
|
||||
while true; do
|
||||
if ! read -rp "Container ID [auto]: " CTID; then
|
||||
msg_err "No input available for prompt: Container ID"
|
||||
return 1
|
||||
fi
|
||||
CTID="$(sanitize_input "$CTID")"
|
||||
if [[ -z "$CTID" ]]; then
|
||||
CTID=$(pvesh get /cluster/nextid)
|
||||
break
|
||||
fi
|
||||
is_uint "$CTID" && break
|
||||
msg_warn "Invalid value: '$CTID' — please retry (digits only)."
|
||||
done
|
||||
else
|
||||
require_valid CTID is_uint "Container ID"
|
||||
fi
|
||||
require_valid CTID is_uint "Container ID"
|
||||
echo " → CTID: $CTID"
|
||||
|
||||
if [[ -z "${CT_HOSTNAME:-}" ]]; then
|
||||
@@ -266,7 +294,12 @@ prompt_lxc_config() {
|
||||
# 4. DHCP, no profile → inherit from host
|
||||
apply_network_profile
|
||||
if [[ -n "${NAMESERVER+x}" ]]; then
|
||||
: # explicit override from env, leave untouched
|
||||
# Explizites Override aus env: "" = inherit bleibt erlaubt, aber ein
|
||||
# gesetzter Wert wird validiert (Review-Finding: lief vorher ungeprüft
|
||||
# bis in pct create).
|
||||
if [[ -n "${NAMESERVER:-}" ]]; then
|
||||
require_valid NAMESERVER is_ipv4_list "DNS server"
|
||||
fi
|
||||
elif [[ -n "$PROFILE_DNS" ]]; then
|
||||
NAMESERVER="$PROFILE_DNS"
|
||||
msg_info "DNS for VLAN ${VLAN_TAG:-none} (${PROFILE_SUBNET:-?}): $NAMESERVER"
|
||||
|
||||
Reference in New Issue
Block a user