From 69364c958f9f2b7430bfb5a072ffe5267bfcc378 Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Tue, 6 May 2025 21:49:24 +0200 Subject: [PATCH] fix: Trim name on rename (#27332) --- lib/extension/bridge.ts | 5 +++-- lib/util/utils.ts | 6 ++++++ test/extensions/bridge.test.ts | 18 ++++++++++++++++++ test/utils.test.ts | 7 ++++++- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index c7258664d..248f549e3 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -19,7 +19,7 @@ import Device from "../model/device"; import data from "../util/data"; import logger from "../util/logger"; import * as settings from "../util/settings"; -import utils from "../util/utils"; +import utils, {assertString} from "../util/utils"; import Extension from "./extension"; const REQUEST_REGEX = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`); @@ -564,7 +564,8 @@ export default class Bridge extends Extension { } const from = deviceAndHasLast ? this.lastJoinedDeviceIeeeAddr : message.from; - const to = message.to; + assertString(message.to, "to"); + const to = message.to.trim(); const homeAssisantRename = message.homeassistant_rename !== undefined ? message.homeassistant_rename : false; const entity = this.getEntity(entityType, from); const oldFriendlyName = entity.options.friendly_name; diff --git a/lib/util/utils.ts b/lib/util/utils.ts index 79b49fec1..49123f4bc 100644 --- a/lib/util/utils.ts +++ b/lib/util/utils.ts @@ -340,6 +340,12 @@ export function isLightExpose(expose: zhc.Expose): expose is zhc.Light { return expose.type === "light"; } +export function assertString(value: unknown, property: string): asserts value is string { + if (typeof value !== "string") { + throw new Error(`${property} is not a string, got ${typeof value} (${value})`); + } +} + function getScenes(entity: zh.Endpoint | zh.Group): Zigbee2MQTTScene[] { const scenes: {[id: number]: Zigbee2MQTTScene} = {}; const endpoints = isZHEndpoint(entity) ? [entity] : entity.members; diff --git a/test/extensions/bridge.test.ts b/test/extensions/bridge.test.ts index 13fbb595f..b6e58be3e 100644 --- a/test/extensions/bridge.test.ts +++ b/test/extensions/bridge.test.ts @@ -2994,6 +2994,24 @@ describe("Extension: Bridge", () => { ); }); + it("Should trim input when renaming device", async () => { + mockMQTTPublishAsync.mockClear(); + mockMQTTEvents.message("zigbee2mqtt/bridge/request/device/rename", stringify({from: "bulb", to: " bulb_new_name "})); + await flushPromises(); + expect(settings.getDevice("bulb")).toBeUndefined(); + expect(settings.getDevice("bulb_new_name")).toStrictEqual({ + ID: "0x000b57fffec6a5b2", + friendly_name: "bulb_new_name", + retain: true, + description: "this is my bulb", + }); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( + "zigbee2mqtt/bridge/response/device/rename", + stringify({data: {from: "bulb", to: "bulb_new_name", homeassistant_rename: false}, status: "ok"}), + {}, + ); + }); + it("Shouldnt allow rename device with to not allowed name containing a wildcard", async () => { mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message("zigbee2mqtt/bridge/request/device/rename", stringify({from: "bulb", to: "living_room/blinds#"})); diff --git a/test/utils.test.ts b/test/utils.test.ts index d72ed555d..66fdf1269 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -2,7 +2,7 @@ import {exec} from "node:child_process"; import fs from "node:fs"; import path from "node:path"; -import utils from "../lib/util/utils"; +import utils, {assertString} from "../lib/util/utils"; // keep the implementations, just spy vi.mock("node:child_process", {spy: true}); @@ -78,6 +78,11 @@ describe("Utils", () => { expect(utils.formatDate(date, "ISO_8601_local").toString().endsWith("+01:00")).toBeTruthy(); }); + it("Assert string", () => { + assertString("test", "property"); + expect(() => assertString(1, "property")).toThrow("property is not a string, got number (1)"); + }); + it("Removes null properties from object", () => { const obj1 = { ab: 0,