fix: LLDP input validation, mgmt_ip early validation, poll timer cleanup, monitor backoff
Lint / Python (flake8) (push) Failing after 41s
Lint / JS (eslint) (push) Successful in 8s
Security / Python Security (bandit) (push) Successful in 42s
Test / Python Tests (pytest) (push) Failing after 1m35s
Lint / Notify on failure (push) Successful in 5s
Lint / Deploy (push) Has been skipped

- app.py: validate server_name from LLDP with fullmatch before use in logs/lookups (prevents log injection)
- app.py: validate each mgmt_ip candidate before assigning host_ip (avoids assigning non-IP string that then fails later check)
- app.py: log actual exception in link_stats JSON parse error
- inspector.html: clear _diagPollTimer in closePanel() so timer doesn't orphan when panel is closed mid-poll
- monitor.py: sleep 30s after a monitor loop exception before resuming normal poll interval

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-11 08:45:28 -04:00
parent 25baec67ac
commit 61408645a5
3 changed files with 15 additions and 5 deletions
+13 -5
View File
@@ -371,8 +371,8 @@ def api_diagnose_start():
return jsonify({'error': 'No link_stats data available'}), 503
try:
link_data = json.loads(raw)
except Exception:
logger.error('Failed to parse link_stats JSON in /api/diagnose')
except Exception as e:
logger.error(f'Failed to parse link_stats JSON in /api/diagnose: {e}')
return jsonify({'error': 'Internal data error'}), 500
switches = link_data.get('unifi_switches', {})
@@ -396,6 +396,9 @@ def api_diagnose_start():
return jsonify({'error': 'No LLDP neighbor data for this port'}), 400
server_name = lldp['system_name']
if not re.fullmatch(r'[a-zA-Z0-9._-]+', server_name):
logger.error(f'Refusing diagnostic: invalid server_name from LLDP: {server_name!r}')
return jsonify({'error': 'LLDP neighbor name contains invalid characters'}), 400
lldp_port_id = lldp.get('port_id', '')
# Find matching host + interface in link_stats hosts
@@ -421,9 +424,14 @@ def api_diagnose_start():
# Resolve host IP from link_stats host data
host_ip = (server_ifaces.get(matched_iface) or {}).get('host_ip')
if not host_ip:
# Fallback: use LLDP mgmt IPs
mgmt_ips = lldp.get('mgmt_ips') or []
host_ip = mgmt_ips[0] if mgmt_ips else None
# Fallback: use first valid IP from LLDP mgmt IPs
for candidate in (lldp.get('mgmt_ips') or []):
try:
ipaddress.ip_address(candidate)
host_ip = candidate
break
except ValueError:
continue
if not host_ip:
return jsonify({'error': 'Cannot determine host IP for SSH'}), 400
+1
View File
@@ -966,6 +966,7 @@ class NetworkMonitor:
except Exception as e:
logger.error(f'Monitor loop error: {e}', exc_info=True)
time.sleep(30)
time.sleep(self.poll_interval)
+1
View File
@@ -231,6 +231,7 @@ function selectPort(el) {
}
function closePanel() {
if (_diagPollTimer) { clearInterval(_diagPollTimer); _diagPollTimer = null; }
document.getElementById('inspector-panel').classList.remove('open');
document.querySelectorAll('.switch-port-block.selected')
.forEach(el => el.classList.remove('selected'));