Files
bambuddy/backend/app/services/network_utils.py
T
maziggy 53bd4fadb3 Fix safe security findings: hashlib, log injection, broad excepts
- Add usedforsecurity=False to MD5 (AMS fingerprint) and SHA1 (git blob
  hash) calls to silence Bandit B303 / CodeQL weak-crypto findings
- Convert ~996 f-string logging calls to parameterized %s-style across
  55 files to prevent log injection (Bandit G201 / CodeQL log-injection)
- Narrow ~199 broad except Exception blocks to specific types:
  OperationalError for DB migrations, OSError for network/file cleanup,
  (OSError, ftplib.error_reply) for FTP, and targeted tuples for
  ZIP/XML/JSON parsing — 36 intentionally left broad (mixed async,
  re-raise patterns)
2026-02-06 11:37:59 +01:00

120 lines
3.5 KiB
Python

"""Network utility functions for interface detection."""
import ipaddress
import logging
import socket
import struct
logger = logging.getLogger(__name__)
# Interfaces to exclude from selection
EXCLUDED_INTERFACE_PREFIXES = ("lo", "docker", "br-", "veth", "virbr")
def get_network_interfaces() -> list[dict]:
"""Get all network interfaces with their IPs and subnets.
Returns:
List of dicts with name, ip, netmask, subnet, broadcast
"""
interfaces = []
try:
import fcntl
for iface in socket.if_nameindex():
name = iface[1]
# Skip excluded interfaces
if any(name.startswith(prefix) for prefix in EXCLUDED_INTERFACE_PREFIXES):
continue
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Get IP address
ip_bytes = fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack("256s", name[:15].encode()),
)[20:24]
ip = socket.inet_ntoa(ip_bytes)
# Get netmask
netmask_bytes = fcntl.ioctl(
s.fileno(),
0x891B, # SIOCGIFNETMASK
struct.pack("256s", name[:15].encode()),
)[20:24]
netmask = socket.inet_ntoa(netmask_bytes)
# Calculate subnet
network = ipaddress.IPv4Network(f"{ip}/{netmask}", strict=False)
interfaces.append(
{
"name": name,
"ip": ip,
"netmask": netmask,
"subnet": str(network),
}
)
s.close()
except OSError:
# Interface doesn't have an IP or other error
pass
except Exception as e:
logger.debug("Error getting info for interface %s: %s", name, e)
except ImportError:
# fcntl not available (Windows)
logger.warning("fcntl not available, interface detection limited")
except Exception as e:
logger.error("Error enumerating interfaces: %s", e)
return interfaces
def find_interface_for_ip(target_ip: str) -> dict | None:
"""Find which interface is on the same subnet as the target IP.
Args:
target_ip: IP address to find the matching interface for
Returns:
Interface dict or None if not found
"""
try:
target = ipaddress.IPv4Address(target_ip)
except ValueError:
logger.error("Invalid target IP: %s", target_ip)
return None
interfaces = get_network_interfaces()
for iface in interfaces:
try:
network = ipaddress.IPv4Network(iface["subnet"], strict=False)
if target in network:
logger.debug("Found interface %s (%s) for target %s", iface["name"], iface["ip"], target_ip)
return iface
except ValueError:
continue
logger.warning("No interface found for target IP %s", target_ip)
return None
def get_other_interfaces(exclude_ip: str) -> list[dict]:
"""Get all interfaces except the one with the given IP.
Args:
exclude_ip: IP address of interface to exclude
Returns:
List of interface dicts
"""
interfaces = get_network_interfaces()
return [iface for iface in interfaces if iface["ip"] != exclude_ip]