Skip to content

Commit d87e20e

Browse files
bwp91claude
andcommitted
test: cover aid.iid format validation
Adds tests verifying the guards from ec93c50: - HAPServer GET /characteristics rejects malformed `?id=` entries (non-numeric, missing dot, too many dots, empty parts) with 400 + HAPStatus.INVALID_VALUE_IN_REQUEST, both for a single bad entry and inside a comma-separated list. - Accessory.handleHAPConnectionClosed skips registered events that do not contain a dot separator, instead of feeding an undefined index to parseInt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 29daf53 commit d87e20e

3 files changed

Lines changed: 53 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file.
1515
- test: cover SEQUENCE_NUM presence check in pair handlers
1616
- test: cover M1 reset prevention in pair-setup
1717
- test: cover safe accessory lookups in slow/timeout warnings
18+
- test: cover aid.iid format validation
1819

1920
### Homebridge Dependencies
2021

src/lib/Accessory.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,33 @@ describe("Accessory", () => {
12031203
});
12041204
});
12051205

1206+
describe("aid.iid format validation in handleHAPConnectionClosed (fix ec93c504)", () => {
1207+
test("should skip registered events without a dot separator", () => {
1208+
const clearMock = jest.fn();
1209+
const mockConnection = {
1210+
getRegisteredEvents: jest.fn().mockReturnValue(new Set([
1211+
"malformed", // no dot — without the fix this triggers parseInt(undefined)
1212+
`${aid}.${iids.on}`, // valid
1213+
])),
1214+
clearRegisteredEvents: clearMock,
1215+
} as unknown as HAPConnection;
1216+
1217+
// @ts-expect-error: private access
1218+
expect(() => accessory.handleHAPConnectionClosed(mockConnection)).not.toThrow();
1219+
expect(clearMock).toHaveBeenCalled();
1220+
});
1221+
1222+
test("should skip empty registered event strings", () => {
1223+
const mockConnection = {
1224+
getRegisteredEvents: jest.fn().mockReturnValue(new Set(["", `${aid}.${iids.on}`])),
1225+
clearRegisteredEvents: jest.fn(),
1226+
} as unknown as HAPConnection;
1227+
1228+
// @ts-expect-error: private access
1229+
expect(() => accessory.handleHAPConnectionClosed(mockConnection)).not.toThrow();
1230+
});
1231+
});
1232+
12061233
describe("non-null assertions in accessory lookups (fix 03b49f9f)", () => {
12071234
test("slow read / timeout warning should not crash for unknown aid.iid in request", async () => {
12081235
jest.useFakeTimers({ doNotFake: ["nextTick", "queueMicrotask"] });

src/lib/HAPServer.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,31 @@ describe("HAPServer", () => {
629629
});
630630
});
631631

632+
describe("GET /characteristics aid.iid format validation (fix ec93c504)", () => {
633+
test.each([
634+
["non-numeric aid", "abc.5"],
635+
["non-numeric iid", "1.xyz"],
636+
["missing dot", "1"],
637+
["too many dots", "1.2.3"],
638+
["empty parts", "."],
639+
["empty aid", ".5"],
640+
["empty iid", "1."],
641+
["whitespace", "1. 5"],
642+
])("should reject id=%s (%s) with INVALID_VALUE_IN_REQUEST", async (_label, idValue) => {
643+
const httpResponse = await client.writeHTTPRequest("GET", `/characteristics?id=${encodeURIComponent(idValue)}`);
644+
expect(httpResponse.statusCode).toBe(HAPHTTPCode.BAD_REQUEST);
645+
const body = JSON.parse(httpResponse.body.toString());
646+
expect(body.status).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST);
647+
});
648+
649+
test("should reject when one entry in a comma-separated list is malformed", async () => {
650+
const httpResponse = await client.writeHTTPRequest("GET", "/characteristics?id=1.5,abc.def");
651+
expect(httpResponse.statusCode).toBe(HAPHTTPCode.BAD_REQUEST);
652+
const body = JSON.parse(httpResponse.body.toString());
653+
expect(body.status).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST);
654+
});
655+
});
656+
632657
test("test /accessories", async () => {
633658
const accessoryResponse: AccessoriesResponse = {
634659
accessories: [{

0 commit comments

Comments
 (0)