Skip to content

Commit 9863d34

Browse files
FI-153Diegorro98joostlekMartinHjelmare
authored
Prevent empty aliases in registries (home-assistant#156061)
Co-authored-by: J. Diego Rodríguez Royo <[email protected]> Co-authored-by: Joost Lekkerkerker <[email protected]> Co-authored-by: Martin Hjelmare <[email protected]>
1 parent f85a684 commit 9863d34

File tree

6 files changed

+208
-10
lines changed

6 files changed

+208
-10
lines changed

homeassistant/components/config/area_registry.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ def websocket_create_area(
6565
data.pop("id")
6666

6767
if "aliases" in data:
68-
# Convert aliases to a set
69-
data["aliases"] = set(data["aliases"])
68+
# Create a set for the aliases without:
69+
# - Empty strings
70+
# - Trailing and leading whitespace characters in the individual aliases
71+
data["aliases"] = {s_strip for s in data["aliases"] if (s_strip := s.strip())}
7072

7173
if "labels" in data:
7274
# Convert labels to a set
@@ -133,8 +135,10 @@ def websocket_update_area(
133135
data.pop("id")
134136

135137
if "aliases" in data:
136-
# Convert aliases to a set
137-
data["aliases"] = set(data["aliases"])
138+
# Create a set for the aliases without:
139+
# - Empty strings
140+
# - Trailing and leading whitespace characters in the individual aliases
141+
data["aliases"] = {s_strip for s in data["aliases"] if (s_strip := s.strip())}
138142

139143
if "labels" in data:
140144
# Convert labels to a set

homeassistant/components/config/entity_registry.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,10 @@ def websocket_update_entity(
227227
changes[key] = msg[key]
228228

229229
if "aliases" in msg:
230-
# Convert aliases to a set
231-
changes["aliases"] = set(msg["aliases"])
230+
# Create a set for the aliases without:
231+
# - Empty strings
232+
# - Trailing and leading whitespace characters in the individual aliases
233+
changes["aliases"] = {s_strip for s in msg["aliases"] if (s_strip := s.strip())}
232234

233235
if "labels" in msg:
234236
# Convert labels to a set

homeassistant/components/config/floor_registry.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ def websocket_create_floor(
6161
data.pop("id")
6262

6363
if "aliases" in data:
64-
# Convert aliases to a set
65-
data["aliases"] = set(data["aliases"])
64+
# Create a set for the aliases without:
65+
# - Empty strings
66+
# - Trailing and leading whitespace characters in the individual aliases
67+
data["aliases"] = {s_strip for s in data["aliases"] if (s_strip := s.strip())}
6668

6769
try:
6870
entry = registry.async_create(**data)
@@ -117,8 +119,10 @@ def websocket_update_floor(
117119
data.pop("id")
118120

119121
if "aliases" in data:
120-
# Convert aliases to a set
121-
data["aliases"] = set(data["aliases"])
122+
# Create a set for the aliases without:
123+
# - Empty strings
124+
# - Trailing and leading whitespace characters in the individual aliases
125+
data["aliases"] = {s_strip for s in data["aliases"] if (s_strip := s.strip())}
122126

123127
try:
124128
entry = registry.async_update(**data)

tests/components/config/test_area_registry.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,39 @@ async def test_create_area(
172172
}
173173
assert len(area_registry.areas) == 2
174174

175+
# Create area with invalid aliases
176+
await client.send_json_auto_id(
177+
{
178+
"aliases": [" alias_1 ", "", " "],
179+
"floor_id": "first_floor",
180+
"icon": "mdi:garage",
181+
"labels": ["label_1", "label_2"],
182+
"name": "mock 3",
183+
"picture": "/image/example.png",
184+
"temperature_entity_id": "sensor.mock_temperature",
185+
"humidity_entity_id": "sensor.mock_humidity",
186+
"type": "config/area_registry/create",
187+
}
188+
)
189+
190+
msg = await client.receive_json()
191+
192+
assert msg["success"]
193+
assert msg["result"] == {
194+
"aliases": unordered(["alias_1"]),
195+
"area_id": ANY,
196+
"floor_id": "first_floor",
197+
"icon": "mdi:garage",
198+
"labels": unordered(["label_1", "label_2"]),
199+
"name": "mock 3",
200+
"picture": "/image/example.png",
201+
"created_at": utcnow().timestamp(),
202+
"modified_at": utcnow().timestamp(),
203+
"temperature_entity_id": "sensor.mock_temperature",
204+
"humidity_entity_id": "sensor.mock_humidity",
205+
}
206+
assert len(area_registry.areas) == 3
207+
175208

176209
async def test_create_area_with_name_already_in_use(
177210
client: MockHAClientWebSocket, area_registry: ar.AreaRegistry
@@ -304,6 +337,40 @@ async def test_update_area(
304337
}
305338
assert len(area_registry.areas) == 1
306339

340+
modified_at = datetime.fromisoformat("2024-07-16T13:55:00.900075+00:00")
341+
freezer.move_to(modified_at)
342+
343+
await client.send_json_auto_id(
344+
{
345+
"type": "config/area_registry/update",
346+
"aliases": ["alias_1", "", " ", " alias_2 "],
347+
"area_id": area.id,
348+
"floor_id": None,
349+
"humidity_entity_id": None,
350+
"icon": None,
351+
"labels": [],
352+
"picture": None,
353+
"temperature_entity_id": None,
354+
}
355+
)
356+
357+
msg = await client.receive_json()
358+
359+
assert msg["result"] == {
360+
"aliases": unordered(["alias_1", "alias_2"]),
361+
"area_id": area.id,
362+
"floor_id": None,
363+
"icon": None,
364+
"labels": [],
365+
"name": "mock 2",
366+
"picture": None,
367+
"temperature_entity_id": None,
368+
"humidity_entity_id": None,
369+
"created_at": created_at.timestamp(),
370+
"modified_at": modified_at.timestamp(),
371+
}
372+
assert len(area_registry.areas) == 1
373+
307374

308375
async def test_update_area_with_same_name(
309376
client: MockHAClientWebSocket, area_registry: ar.AreaRegistry

tests/components/config/test_entity_registry.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,49 @@ async def test_update_entity(
887887
},
888888
}
889889

890+
# Add illegal terms to aliases
891+
await client.send_json_auto_id(
892+
{
893+
"type": "config/entity_registry/update",
894+
"entity_id": "test_domain.world",
895+
"aliases": ["alias_1", "alias_2", "", " alias_3 ", " "],
896+
}
897+
)
898+
899+
msg = await client.receive_json()
900+
assert msg["success"]
901+
902+
assert msg["result"] == {
903+
"entity_entry": {
904+
"aliases": unordered(["alias_1", "alias_2", "alias_3"]),
905+
"area_id": "mock-area-id",
906+
"capabilities": None,
907+
"categories": {"scope1": "id", "scope3": "other_id"},
908+
"config_entry_id": None,
909+
"config_subentry_id": None,
910+
"created_at": created.timestamp(),
911+
"device_class": "custom_device_class",
912+
"device_id": None,
913+
"disabled_by": None,
914+
"entity_category": None,
915+
"entity_id": "test_domain.world",
916+
"has_entity_name": False,
917+
"hidden_by": "user", # We exchange strings over the WS API, not enums
918+
"icon": "icon:after update",
919+
"id": ANY,
920+
"labels": unordered(["label1", "label2"]),
921+
"modified_at": modified.timestamp(),
922+
"name": "after update",
923+
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
924+
"original_device_class": None,
925+
"original_icon": None,
926+
"original_name": None,
927+
"platform": "test_platform",
928+
"translation_key": None,
929+
"unique_id": "1234",
930+
},
931+
}
932+
890933

891934
async def test_update_entity_require_restart(
892935
hass: HomeAssistant, client: MockHAClientWebSocket, freezer: FrozenDateTimeFactory

tests/components/config/test_floor_registry.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,30 @@ async def test_create_floor(
122122
"level": 2,
123123
}
124124

125+
# Floor with invalid aliases
126+
await client.send_json_auto_id(
127+
{
128+
"name": "Third floor",
129+
"type": "config/floor_registry/create",
130+
"aliases": ["", " "],
131+
"icon": "mdi:home-floor-2",
132+
"level": 3,
133+
}
134+
)
135+
136+
msg = await client.receive_json()
137+
138+
assert len(floor_registry.floors) == 3
139+
assert msg["result"] == {
140+
"aliases": [],
141+
"created_at": utcnow().timestamp(),
142+
"icon": "mdi:home-floor-2",
143+
"floor_id": "third_floor",
144+
"modified_at": utcnow().timestamp(),
145+
"name": "Third floor",
146+
"level": 3,
147+
}
148+
125149

126150
async def test_create_floor_with_name_already_in_use(
127151
client: MockHAClientWebSocket,
@@ -249,6 +273,60 @@ async def test_update_floor(
249273
"level": None,
250274
}
251275

276+
# Add invalid aliases
277+
modified_at = datetime.fromisoformat("2024-07-16T13:55:00.900075+00:00")
278+
freezer.move_to(modified_at)
279+
await client.send_json_auto_id(
280+
{
281+
"floor_id": floor.floor_id,
282+
"name": "First floor",
283+
"aliases": ["top floor", "attic", "", " "],
284+
"icon": None,
285+
"level": None,
286+
"type": "config/floor_registry/update",
287+
}
288+
)
289+
290+
msg = await client.receive_json()
291+
292+
assert len(floor_registry.floors) == 1
293+
assert msg["result"] == {
294+
"aliases": unordered(["top floor", "attic"]),
295+
"created_at": created_at.timestamp(),
296+
"icon": None,
297+
"floor_id": floor.floor_id,
298+
"modified_at": modified_at.timestamp(),
299+
"name": "First floor",
300+
"level": None,
301+
}
302+
303+
# Add alias with trailing and leading whitespaces
304+
modified_at = datetime.fromisoformat("2024-07-16T13:55:00.900075+00:00")
305+
freezer.move_to(modified_at)
306+
await client.send_json_auto_id(
307+
{
308+
"floor_id": floor.floor_id,
309+
"name": "First floor",
310+
"aliases": ["top floor", "attic", "solaio "],
311+
"icon": None,
312+
"level": None,
313+
"type": "config/floor_registry/update",
314+
}
315+
)
316+
317+
msg = await client.receive_json()
318+
319+
assert len(floor_registry.floors) == 1
320+
assert msg["result"] == {
321+
"aliases": unordered(["top floor", "attic", "solaio"]),
322+
"created_at": created_at.timestamp(),
323+
"icon": None,
324+
"floor_id": floor.floor_id,
325+
"modified_at": modified_at.timestamp(),
326+
"name": "First floor",
327+
"level": None,
328+
}
329+
252330

253331
async def test_update_with_name_already_in_use(
254332
client: MockHAClientWebSocket,

0 commit comments

Comments
 (0)