Merge pull request #203 from jcastro/feature/fix-open-bug-issues

Fix webhook loopback detection and update handoff

Thanks for the thorough work here, jcastro — really appreciate the bug triage on top of the fix itself, it makes reviewing much easier.
The changes look good to me:
	•	_is_loopback_addr() is a clean solution. Using the stdlib ipaddress module and explicitly handling the IPv4-mapped IPv6 case (::ffff:127.0.0.1) is exactly what was needed after the dual-stack binding change. The docstring explaining why is a nice touch.
	•	The exec bash swap in menu is the right fix for #180. Replacing the shell before the installer overwrites /usr/local/bin/menu avoids the half-old/half-new parsing that was causing the syntax error on line 105. Good catch removing the now-unreachable return 0 too.
Validation looks solid (8/8 loopback cases, syntax checks on all scripts).
Merging into develop. Thanks again! 🙌
This commit is contained in:
MacRimi
2026-05-14 17:25:59 +02:00
committed by GitHub
2 changed files with 23 additions and 5 deletions
+19 -1
View File
@@ -191,6 +191,24 @@ def _bad_request(msg: str):
return jsonify({'error': msg}), 400
def _is_loopback_addr(value: str) -> bool:
"""Return True for IPv4, IPv6 and IPv4-mapped loopback addresses.
When Flask is bound to ``::`` for dual-stack support, an HTTP request
sent to ``127.0.0.1`` can be reported as ``::ffff:127.0.0.1``. Treat it
as local so the PVE webhook keeps the intended localhost trust path.
"""
try:
import ipaddress
addr = ipaddress.ip_address(value)
if addr.is_loopback:
return True
ipv4_mapped = getattr(addr, 'ipv4_mapped', None)
return bool(ipv4_mapped and ipv4_mapped.is_loopback)
except ValueError:
return value == 'localhost'
def _validate_event_type(value: str) -> bool:
return isinstance(value, str) and bool(_EVENT_TYPE_RE.match(value))
@@ -1225,7 +1243,7 @@ def proxmox_webhook():
_reject = lambda code, error, status: (jsonify({'accepted': False, 'error': error}), status)
client_ip = request.remote_addr or ''
is_localhost = client_ip in ('127.0.0.1', '::1')
is_localhost = _is_loopback_addr(client_ip)
# CSRF defence-in-depth: reject `application/x-www-form-urlencoded`
# bodies. PVE always sends `application/json`; form-encoded bodies
+4 -4
View File
@@ -79,8 +79,8 @@ check_updates_stable() {
if curl -fsSL "$INSTALL_URL" -o "$INSTALL_SCRIPT"; then
chmod +x "$INSTALL_SCRIPT"
bash "$INSTALL_SCRIPT" --update
return 0
# Replace this shell before the installer refreshes /usr/local/bin/menu.
exec bash "$INSTALL_SCRIPT" --update
fi
fi
}
@@ -111,8 +111,8 @@ check_updates_beta() {
local INSTALL_BETA_SCRIPT="$BASE_DIR/install_proxmenux_beta.sh"
if curl -fsSL "$REPO_DEVELOP/install_proxmenux_beta.sh" -o "$INSTALL_BETA_SCRIPT"; then
chmod +x "$INSTALL_BETA_SCRIPT"
bash "$INSTALL_BETA_SCRIPT" --update
return 0
# Replace this shell before the installer refreshes /usr/local/bin/menu.
exec bash "$INSTALL_BETA_SCRIPT" --update
else
msg_error "Could not download the beta installer from the develop branch."
fi