Handle invalid IP addresses in ip_bans.yaml gracefully (#157232)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
Franck Nijhof
2025-11-25 14:49:05 +01:00
committed by GitHub
parent 2cd0637324
commit c2219aadb1
2 changed files with 58 additions and 0 deletions

View File

@@ -233,6 +233,9 @@ class IpBanManager:
except vol.Invalid as err: except vol.Invalid as err:
_LOGGER.error("Failed to load IP ban %s: %s", ip_info, err) _LOGGER.error("Failed to load IP ban %s: %s", ip_info, err)
continue continue
except ValueError:
_LOGGER.error("Failed to load IP ban: invalid IP address %s", ip_ban)
continue
self.ip_bans_lookup = ip_bans_lookup self.ip_bans_lookup = ip_bans_lookup

View File

@@ -2,6 +2,7 @@
from http import HTTPStatus from http import HTTPStatus
from ipaddress import ip_address from ipaddress import ip_address
import logging
import os import os
from unittest.mock import AsyncMock, Mock, mock_open, patch from unittest.mock import AsyncMock, Mock, mock_open, patch
@@ -113,6 +114,60 @@ async def test_access_from_banned_ip_with_partially_broken_yaml_file(
assert "Failed to load IP ban" in caplog.text assert "Failed to load IP ban" in caplog.text
async def test_access_from_banned_ip_with_invalid_ip_entry(
hass: HomeAssistant,
aiohttp_client: ClientSessionGenerator,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that invalid IP addresses in ban file are skipped gracefully.
An invalid IP entry (e.g., with typo like "Eo128.199.160.243") should
be logged as an error and skipped, allowing valid bans to still load.
The test ensures that valid IPs after invalid ones are still processed.
"""
app = web.Application()
app[KEY_HASS] = hass
setup_bans(hass, app, 5)
set_real_ip = mock_real_ip(app)
# Invalid IPs interspersed between valid ones to ensure continue works
data = {
"Eo128.199.160.243": {"banned_at": "2024-07-06T14:07:46"},
BANNED_IPS[0]: {"banned_at": "2016-11-16T19:20:03"},
"invalidip": {"banned_at": "2024-07-06T14:07:46"},
BANNED_IPS[1]: {"banned_at": "2016-11-16T19:20:03"},
}
with patch(
"homeassistant.components.http.ban.load_yaml_config_file",
return_value=data,
):
client = await aiohttp_client(app)
# Verify exactly 2 valid IPs were loaded (invalid ones skipped)
manager = app[KEY_BAN_MANAGER]
assert len(manager.ip_bans_lookup) == len(BANNED_IPS)
# Valid banned IPs should still be blocked (even though they came after invalid ones)
for remote_addr in BANNED_IPS:
set_real_ip(remote_addr)
resp = await client.get("/")
assert resp.status == HTTPStatus.FORBIDDEN
# Non-banned IP should have access
set_real_ip("192.168.1.1")
resp = await client.get("/")
assert resp.status == HTTPStatus.NOT_FOUND
# Check that both invalid IP entries were logged
for ip in ("Eo128.199.160.243", "invalidip"):
assert (
"homeassistant.components.http.ban",
logging.ERROR,
f"Failed to load IP ban: invalid IP address {ip}",
) in caplog.record_tuples
async def test_no_ip_bans_file( async def test_no_ip_bans_file(
hass: HomeAssistant, aiohttp_client: ClientSessionGenerator hass: HomeAssistant, aiohttp_client: ClientSessionGenerator
) -> None: ) -> None: