Skip to content

Commit 0416816

Browse files
bwp91claude
andcommitted
test: cover M1 reset prevention in pair-setup
Adds two HAPServer integration tests verifying the guard from d4c81be: - a second M1 on a connection with in-progress pair-setup state is rejected with 400 BAD_REQUEST + STATE=M2 + UNKNOWN, rather than restarting setup and overwriting the in-progress SRP server. - a fresh connection can still start M1 normally — the guard does not leak across connections. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f9e07e6 commit 0416816

2 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
@@ -13,6 +13,7 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file.
1313
- test: cover required TLV field validation in pairing handlers
1414
- test: cover RTP proxy setup rejection handling
1515
- test: cover SEQUENCE_NUM presence check in pair handlers
16+
- test: cover M1 reset prevention in pair-setup
1617

1718
### Homebridge Dependencies
1819

src/lib/HAPServer.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,58 @@ describe("HAPServer", () => {
328328
});
329329
});
330330

331+
describe("M1 reset prevention (fix d4c81be0)", () => {
332+
test("/pair-setup should reject a second M1 on a connection with in-progress state", async () => {
333+
server = new HAPServer(accessoryInfoUnpaired);
334+
const [port] = await bindServer(server);
335+
336+
const pairSetup = new PairSetupClient(port, httpAgent);
337+
338+
// first M1 succeeds → connection state advances to M2
339+
const firstResponse = await pairSetup.sendM1();
340+
const firstObjects = tlv.decode(firstResponse.data);
341+
expect(firstObjects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2);
342+
expect(firstObjects[TLVValues.ERROR_CODE]).toBeUndefined();
343+
344+
// second M1 on the same connection — without the guard this would
345+
// restart pair-setup and overwrite the in-progress SRP server
346+
try {
347+
await pairSetup.sendM1();
348+
fail("Expected BAD_REQUEST response");
349+
} catch (error) {
350+
expect(error).toBeInstanceOf(AxiosError);
351+
expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST);
352+
const objects = tlv.decode(error.response?.data);
353+
// sequence + 1 = M2; UNKNOWN error
354+
expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2);
355+
expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN);
356+
}
357+
});
358+
359+
test("/pair-setup should still accept M1 on a fresh connection after a previous setup completed", async () => {
360+
server = new HAPServer(accessoryInfoUnpaired);
361+
const [port] = await bindServer(server);
362+
363+
// first connection: do an M1 and abort
364+
const firstAgent = new Agent({ keepAlive: true });
365+
const firstClient = new PairSetupClient(port, firstAgent);
366+
await firstClient.sendM1();
367+
firstAgent.destroy();
368+
369+
// second connection: a fresh M1 should still succeed (no state pollution)
370+
const secondAgent = new Agent({ keepAlive: true });
371+
try {
372+
const secondClient = new PairSetupClient(port, secondAgent);
373+
const response = await secondClient.sendM1();
374+
const objects = tlv.decode(response.data);
375+
expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2);
376+
expect(objects[TLVValues.ERROR_CODE]).toBeUndefined();
377+
} finally {
378+
secondAgent.destroy();
379+
}
380+
});
381+
});
382+
331383
describe("SEQUENCE_NUM check in pair handlers (fix ebe2ec67)", () => {
332384
test("/pair-setup should reject when SEQUENCE_NUM TLV is missing", async () => {
333385
server = new HAPServer(accessoryInfoUnpaired);

0 commit comments

Comments
 (0)