|
2 | 2 |
|
3 | 3 | from http import HTTPStatus |
4 | 4 | from ipaddress import ip_address |
| 5 | +import logging |
5 | 6 | import os |
6 | 7 | from unittest.mock import AsyncMock, Mock, mock_open, patch |
7 | 8 |
|
@@ -113,6 +114,60 @@ async def test_access_from_banned_ip_with_partially_broken_yaml_file( |
113 | 114 | assert "Failed to load IP ban" in caplog.text |
114 | 115 |
|
115 | 116 |
|
| 117 | +async def test_access_from_banned_ip_with_invalid_ip_entry( |
| 118 | + hass: HomeAssistant, |
| 119 | + aiohttp_client: ClientSessionGenerator, |
| 120 | + caplog: pytest.LogCaptureFixture, |
| 121 | +) -> None: |
| 122 | + """Test that invalid IP addresses in ban file are skipped gracefully. |
| 123 | +
|
| 124 | + An invalid IP entry (e.g., with typo like "Eo128.199.160.243") should |
| 125 | + be logged as an error and skipped, allowing valid bans to still load. |
| 126 | + The test ensures that valid IPs after invalid ones are still processed. |
| 127 | + """ |
| 128 | + app = web.Application() |
| 129 | + app[KEY_HASS] = hass |
| 130 | + setup_bans(hass, app, 5) |
| 131 | + set_real_ip = mock_real_ip(app) |
| 132 | + |
| 133 | + # Invalid IPs interspersed between valid ones to ensure continue works |
| 134 | + data = { |
| 135 | + "Eo128.199.160.243": {"banned_at": "2024-07-06T14:07:46"}, |
| 136 | + BANNED_IPS[0]: {"banned_at": "2016-11-16T19:20:03"}, |
| 137 | + "invalidip": {"banned_at": "2024-07-06T14:07:46"}, |
| 138 | + BANNED_IPS[1]: {"banned_at": "2016-11-16T19:20:03"}, |
| 139 | + } |
| 140 | + |
| 141 | + with patch( |
| 142 | + "homeassistant.components.http.ban.load_yaml_config_file", |
| 143 | + return_value=data, |
| 144 | + ): |
| 145 | + client = await aiohttp_client(app) |
| 146 | + |
| 147 | + # Verify exactly 2 valid IPs were loaded (invalid ones skipped) |
| 148 | + manager = app[KEY_BAN_MANAGER] |
| 149 | + assert len(manager.ip_bans_lookup) == len(BANNED_IPS) |
| 150 | + |
| 151 | + # Valid banned IPs should still be blocked (even though they came after invalid ones) |
| 152 | + for remote_addr in BANNED_IPS: |
| 153 | + set_real_ip(remote_addr) |
| 154 | + resp = await client.get("/") |
| 155 | + assert resp.status == HTTPStatus.FORBIDDEN |
| 156 | + |
| 157 | + # Non-banned IP should have access |
| 158 | + set_real_ip("192.168.1.1") |
| 159 | + resp = await client.get("/") |
| 160 | + assert resp.status == HTTPStatus.NOT_FOUND |
| 161 | + |
| 162 | + # Check that both invalid IP entries were logged |
| 163 | + for ip in ("Eo128.199.160.243", "invalidip"): |
| 164 | + assert ( |
| 165 | + "homeassistant.components.http.ban", |
| 166 | + logging.ERROR, |
| 167 | + f"Failed to load IP ban: invalid IP address {ip}", |
| 168 | + ) in caplog.record_tuples |
| 169 | + |
| 170 | + |
116 | 171 | async def test_no_ip_bans_file( |
117 | 172 | hass: HomeAssistant, aiohttp_client: ClientSessionGenerator |
118 | 173 | ) -> None: |
|
0 commit comments