diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0fe64758..8fab71446 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: - name: Get Release Tag (Latest Only) if: steps.release-type.outputs.type == 'latest' id: get_version - uses: jannemattila/get-version-from-tag@v3 + uses: jannemattila/get-version-from-tag@v4 - name: Tag Info (Latest Only) if: steps.release-type.outputs.type == 'latest' diff --git a/@types/bonjour-hap.d.ts b/@types/bonjour-hap.d.ts deleted file mode 100644 index 9f152f845..000000000 --- a/@types/bonjour-hap.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -declare module "bonjour-hap" { - - export const enum Protocols { - TCP = "tcp", - UDP = "udp", - } - - export type Nullable = T | null; - export type TxtRecord = Record; - - export class BonjourHAPService { - name: string; - type: string; - subtypes: Nullable; - protocol: Protocols; - host: string; - port: number; - fqdn: string; - txt: Nullable>; - published: boolean; - - start(): void; - stop(callback?: () => void): void; - destroy(): void; - updateTxt(txt: TxtRecord, silent?: boolean): void; - } - - export type PublishOptions = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - category?: any, - host?: string; - name?: string; - pincode?: string; - port: number; - protocol?: Protocols; - subtypes?: string[]; - txt?: Record; - type?: string; - username?: string; - - addUnsafeServiceEnumerationRecord?: boolean, - - restrictedAddresses?: string[]; - disabledIpv6?: boolean; - }; - - export class BonjourHAP { - publish(options: PublishOptions): BonjourHAPService; - unpublishAll(callback: () => void): void; - destroy(): void; - } - - - export type MulticastOptions = { - multicast?: boolean; - interface?: string; - port?: number; - ip?: string; - ttl?: number; - loopback?: boolean; - reuseAddr?: boolean; - }; - function createWithOptions(options?: MulticastOptions): BonjourHAP; - - export default createWithOptions; -} diff --git a/@types/simple-plist.d.ts b/@types/simple-plist.d.ts deleted file mode 100644 index f71beace7..000000000 --- a/@types/simple-plist.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "simple-plist" { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export function readFileSync(path: string): Record; -} diff --git a/CHANGELOG.md b/CHANGELOG.md index b5bf0a8e1..f1f914436 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to `@homebridge/hap-nodejs` will be documented in this file. This project tries to adhere to [Semantic Versioning](http://semver.org/). +## v2.1.7 (Pending Release) + +### Changes + +- chore: dependency updates +- chore: drop local @types shims, use upstream bonjour-hap types +- chore(ci): bump release workflow action versions +- test: cover encrypted data length validation in pair handlers +- test: cover required TLV field validation in pairing handlers +- test: cover RTP proxy setup rejection handling +- test: cover SEQUENCE_NUM presence check in pair handlers +- test: cover M1 reset prevention in pair-setup +- test: cover safe accessory lookups in slow/timeout warnings +- test: cover aid.iid format validation +- test: cover camera stream start TLV parsing guards +- test: cover error argument in pairing debug logs +- test: cover constant-time pincode comparison +- test: cover characteristic warning message for non-Error throws + +### Homebridge Dependencies + +- `@homebridge/ciao` @ `v1.3.9` +- `@homebridge/dbus-native` @ `v0.7.6` +- `bonjour-hap` @ `v3.10.3` + ## v2.1.6 (2026-05-08) ### Changes diff --git a/package-lock.json b/package-lock.json index dc9d7d892..16f0e349a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "2.1.6", "license": "Apache-2.0", "dependencies": { - "@homebridge/ciao": "^1.3.8", - "@homebridge/dbus-native": "^0.7.5", - "bonjour-hap": "^3.10.2", + "@homebridge/ciao": "^1.3.9", + "@homebridge/dbus-native": "^0.7.6", + "bonjour-hap": "^3.10.3", "debug": "^4.4.3", "fast-srp-hap": "^2.0.4", "futoin-hkdf": "^1.5.3", @@ -26,20 +26,20 @@ "@types/debug": "^4.1.13", "@types/escape-html": "^1.0.4", "@types/jest": "^30.0.0", - "@types/node": "^25.6.2", + "@types/node": "^25.9.1", "@types/plist": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^8.59.2", - "@typescript-eslint/parser": "^8.59.2", - "axios": "^1.16.0", + "@typescript-eslint/eslint-plugin": "^8.59.4", + "@typescript-eslint/parser": "^8.59.4", + "axios": "^1.16.1", "commander": "^14.0.3", "escape-html": "^1.0.3", - "eslint": "^10.3.0", + "eslint": "^10.4.0", "http-parser-js": "^0.5.10", - "jest": "^30.4.1", + "jest": "^30.4.2", "rimraf": "^6.1.3", - "semver": "^7.7.4", + "semver": "^7.8.1", "simple-plist": "^1.4.0", - "ts-jest": "^29.4.9", + "ts-jest": "^29.4.11", "ts-node": "^10.9.2", "typedoc": "^0.28.19", "typescript": "^6.0.3" @@ -49,13 +49,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -64,9 +64,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -74,21 +74,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -115,14 +115,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -132,14 +132,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -159,9 +159,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -169,29 +169,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -201,9 +201,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -211,9 +211,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -221,9 +221,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -231,9 +231,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -241,27 +241,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -326,13 +326,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -368,13 +368,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -494,13 +494,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -510,33 +510,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -544,14 +544,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -706,9 +706,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", - "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -815,9 +815,9 @@ } }, "node_modules/@homebridge/ciao": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.8.tgz", - "integrity": "sha512-lNhpCsZVbdbjz2trFjQdzQ3cUIMZQMIMksi7wd3ntTIYgdaGLqT1Ms97DfVIJYHzRuduf56ISvgU8RRLTpK/ng==", + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.9.tgz", + "integrity": "sha512-TMy9zy173jDOpnFXDqL3BPIQn5lfcAkSsivYQatCCakoHk4fLGd7QjfAaNGYE3Ox+/ZI6Lq0e1gGcz1qdw/IbA==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -830,9 +830,9 @@ } }, "node_modules/@homebridge/dbus-native": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.7.5.tgz", - "integrity": "sha512-SX4Mwg7JYED+o5WMz1+Y4FzGPGtXruDcwQt1SCkzfSqMTzrFj3uO5spEMn04Jj2kCqJVcrRQFWbqw+XW8suIXA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.7.6.tgz", + "integrity": "sha512-jvwmRYqe01+sP7XqOu7GdP2lHJhctI9MjRddPvgPb1KvsVKW/RrKjF/8CwTqv1vXkwcJHySpny7PYaiBGwtjRw==", "license": "MIT", "dependencies": { "event-stream": "^4.0.1", @@ -1066,9 +1066,9 @@ } }, "node_modules/@jest/core": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.1.tgz", - "integrity": "sha512-zNfBGtukVyc0ClmSzXgeP6eseumdekHfrqa++GsPK8ZUm9Hm3TY8X8LQvGfZVrp23RSz9ebbcruXnAv3no0Q+g==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1086,14 +1086,14 @@ "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.11", "jest-changed-files": "30.4.1", - "jest-config": "30.4.1", + "jest-config": "30.4.2", "jest-haste-map": "30.4.1", "jest-message-util": "30.4.1", "jest-regex-util": "30.4.0", "jest-resolve": "30.4.1", - "jest-resolve-dependencies": "30.4.1", - "jest-runner": "30.4.1", - "jest-runtime": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", "jest-snapshot": "30.4.1", "jest-util": "30.4.1", "jest-validate": "30.4.1", @@ -1445,16 +1445,22 @@ "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@pkgjs/parseargs": { @@ -1735,13 +1741,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz", - "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/plist": { @@ -1787,17 +1793,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", - "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/type-utils": "8.59.2", - "@typescript-eslint/utils": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1810,7 +1816,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.2", + "@typescript-eslint/parser": "^8.59.4", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1826,16 +1832,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", - "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3" }, "engines": { @@ -1851,14 +1857,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", - "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.2", - "@typescript-eslint/types": "^8.59.2", + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", "debug": "^4.4.3" }, "engines": { @@ -1873,14 +1879,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", - "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2" + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1891,9 +1897,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", - "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", "dev": true, "license": "MIT", "engines": { @@ -1908,15 +1914,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", - "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2", - "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1933,9 +1939,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", - "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", "dev": true, "license": "MIT", "engines": { @@ -1947,16 +1953,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", - "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.2", - "@typescript-eslint/tsconfig-utils": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/visitor-keys": "8.59.2", + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2014,16 +2020,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", - "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.2", - "@typescript-eslint/types": "8.59.2", - "@typescript-eslint/typescript-estree": "8.59.2" + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2038,13 +2044,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", - "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/types": "8.59.4", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2076,9 +2082,9 @@ "license": "ISC" }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", "cpu": [ "arm" ], @@ -2090,9 +2096,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", "cpu": [ "arm64" ], @@ -2104,9 +2110,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", "cpu": [ "arm64" ], @@ -2118,9 +2124,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", "cpu": [ "x64" ], @@ -2132,9 +2138,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", "cpu": [ "x64" ], @@ -2146,9 +2152,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", "cpu": [ "arm" ], @@ -2160,9 +2166,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", "cpu": [ "arm" ], @@ -2174,9 +2180,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", "cpu": [ "arm64" ], @@ -2191,9 +2197,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", "cpu": [ "arm64" ], @@ -2207,10 +2213,44 @@ "linux" ] }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", "cpu": [ "ppc64" ], @@ -2225,9 +2265,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", "cpu": [ "riscv64" ], @@ -2242,9 +2282,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", "cpu": [ "riscv64" ], @@ -2259,9 +2299,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", "cpu": [ "s390x" ], @@ -2276,9 +2316,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", "cpu": [ "x64" ], @@ -2293,9 +2333,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", "cpu": [ "x64" ], @@ -2309,10 +2349,24 @@ "linux" ] }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", "cpu": [ "wasm32" ], @@ -2320,16 +2374,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", "cpu": [ "arm64" ], @@ -2341,9 +2397,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", "cpu": [ "ia32" ], @@ -2355,9 +2411,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", "cpu": [ "x64" ], @@ -2414,6 +2470,19 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -2525,14 +2594,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -2664,9 +2734,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.28", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.28.tgz", - "integrity": "sha512-Ic44hnOtFIgravCunj1ifSoQPSUrkNiJuH9Mf6jr2jjoA74icqV8wU0KuadXeOR8zuIJMOoTv0GuQjZ9ZYNMeA==", + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2687,9 +2757,9 @@ } }, "node_modules/bonjour-hap": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.10.2.tgz", - "integrity": "sha512-37ZEbMSKZ/K4O8sK9YDVFKJ62ZqlK4OPXcBUYaBnVlqr6vQjYwIoFuOQL0ZhztkCNgyyiCExfN4FcZB5cS9t1A==", + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.10.3.tgz", + "integrity": "sha512-KEaj40wsnI7diI44Bonr/XWzSzNoodVG4Uo40sQIsqvyEPxSjix3dyjdryDudrIlBFYJBAvfW57mZ6PRgsMImg==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2829,9 +2899,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001792", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", - "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -3194,9 +3264,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.352", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.352.tgz", - "integrity": "sha512-9wHk8x6dyuimoe18EdiDPWKExNdxYqo4fn4FwOVVper6RxT3cmpBwBkWWfSOCYJjQdIco/nPhJhNLmn4Ufg1Yg==", + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", "dev": true, "license": "ISC" }, @@ -3264,9 +3334,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -3323,16 +3393,16 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.5.5", + "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", @@ -3973,9 +4043,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -4131,6 +4201,20 @@ "dev": true, "license": "MIT" }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4375,16 +4459,16 @@ } }, "node_modules/jest": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.1.tgz", - "integrity": "sha512-ZXSQlP2bAgIq0XmJ49HNmrgXSoWoHEzciSw3YhPbOA3gVMl3CyLdHjbpV+dbR7ggOVwSEo4cl5OOaYwRrmWqEA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.4.1", + "@jest/core": "30.4.2", "@jest/types": "30.4.1", "import-local": "^3.2.0", - "jest-cli": "30.4.1" + "jest-cli": "30.4.2" }, "bin": { "jest": "bin/jest.js" @@ -4417,9 +4501,9 @@ } }, "node_modules/jest-circus": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.1.tgz", - "integrity": "sha512-kcCeuPX8Kh6TSujMOIzaAXWvvr41LFlbhLyEYzcc8doXIuGdX+hOxSxbAH7sJItvi1H2ZOU5B3ujD3FLiX5e4g==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4435,7 +4519,7 @@ "jest-each": "30.4.1", "jest-matcher-utils": "30.4.1", "jest-message-util": "30.4.1", - "jest-runtime": "30.4.1", + "jest-runtime": "30.4.2", "jest-snapshot": "30.4.1", "jest-util": "30.4.1", "p-limit": "^3.1.0", @@ -4449,19 +4533,19 @@ } }, "node_modules/jest-cli": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.1.tgz", - "integrity": "sha512-wh86tmU2ak4aqaVg4Y+OwNys9Plrh4478+o8Zapeo8iz95uwW/WY+A4Yb3pd6C3ilHhY+Ue6V+yDM8G+zUDX+A==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.4.1", + "@jest/core": "30.4.2", "@jest/test-result": "30.4.1", "@jest/types": "30.4.1", "chalk": "^4.1.2", "exit-x": "^0.2.2", "import-local": "^3.2.0", - "jest-config": "30.4.1", + "jest-config": "30.4.2", "jest-util": "30.4.1", "jest-validate": "30.4.1", "yargs": "^17.7.2" @@ -4482,9 +4566,9 @@ } }, "node_modules/jest-config": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.1.tgz", - "integrity": "sha512-AHAI8llsQFfz3oE6/AcBrP7tJdVnqczmBvnXONO8RWRqKefLaxKmkIUq0otc+QTZ/0gxBP7wBLfh6caMmNZcLA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", "dev": true, "license": "MIT", "dependencies": { @@ -4499,12 +4583,12 @@ "deepmerge": "^4.3.1", "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-circus": "30.4.1", + "jest-circus": "30.4.2", "jest-docblock": "30.4.0", "jest-environment-node": "30.4.1", "jest-regex-util": "30.4.0", "jest-resolve": "30.4.1", - "jest-runner": "30.4.1", + "jest-runner": "30.4.2", "jest-util": "30.4.1", "jest-validate": "30.4.1", "parse-json": "^5.2.0", @@ -4738,9 +4822,9 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.1.tgz", - "integrity": "sha512-MstV4pRIfUBs9kuMHSzYHPMPYHqQGoknfRv6tEEpOX7755aaK4Hk5ICwTtOHyjCc3+hYMoxnKF3ENu3iayEIog==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4752,9 +4836,9 @@ } }, "node_modules/jest-runner": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.1.tgz", - "integrity": "sha512-NbStoXGdqMuYF8m7NEQ6FH2gH4eTvcSyz7BINLLuGacdAKtlsDa7PzPSZUtyiBfdMycO2Yeyn3ibfzUl2plRjA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", "dev": true, "license": "MIT", "dependencies": { @@ -4774,7 +4858,7 @@ "jest-leak-detector": "30.4.1", "jest-message-util": "30.4.1", "jest-resolve": "30.4.1", - "jest-runtime": "30.4.1", + "jest-runtime": "30.4.2", "jest-util": "30.4.1", "jest-watcher": "30.4.1", "jest-worker": "30.4.1", @@ -4797,9 +4881,9 @@ } }, "node_modules/jest-runtime": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.1.tgz", - "integrity": "sha512-Ityu3lzs8+it7ABsi7N52Px3Ic1az9w+sBkP/r1xK5MaIq1BdYkYonftpwtX5AtibDSFrKTNEW9KLUXAynXIcA==", + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5081,10 +5165,20 @@ "license": "MIT" }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", + "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" @@ -5176,15 +5270,25 @@ "license": "MIT" }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "linkify-it": "^5.0.1", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -5367,11 +5471,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", - "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -5919,9 +6026,9 @@ } }, "node_modules/rimraf/node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -5991,9 +6098,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "dev": true, "license": "ISC", "bin": { @@ -6432,9 +6539,9 @@ } }, "node_modules/ts-jest": { - "version": "29.4.9", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", - "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", + "version": "29.4.11", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", + "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", "dev": true, "license": "MIT", "dependencies": { @@ -6444,7 +6551,7 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.4", + "semver": "^7.8.0", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -6688,45 +6795,48 @@ } }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.3.0" + "napi-postinstall": "^0.3.4" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "node_modules/update-browserslist-db": { @@ -7001,9 +7111,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", - "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 239033e7f..2282564c5 100644 --- a/package.json +++ b/package.json @@ -49,13 +49,12 @@ "files": [ "README.md", "LICENSE", - "dist", - "@types" + "dist" ], "dependencies": { - "@homebridge/ciao": "^1.3.8", - "@homebridge/dbus-native": "^0.7.5", - "bonjour-hap": "^3.10.2", + "@homebridge/ciao": "^1.3.9", + "@homebridge/dbus-native": "^0.7.6", + "bonjour-hap": "^3.10.3", "debug": "^4.4.3", "fast-srp-hap": "^2.0.4", "futoin-hkdf": "^1.5.3", @@ -70,20 +69,20 @@ "@types/debug": "^4.1.13", "@types/escape-html": "^1.0.4", "@types/jest": "^30.0.0", - "@types/node": "^25.6.2", + "@types/node": "^25.9.1", "@types/plist": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^8.59.2", - "@typescript-eslint/parser": "^8.59.2", - "axios": "^1.16.0", + "@typescript-eslint/eslint-plugin": "^8.59.4", + "@typescript-eslint/parser": "^8.59.4", + "axios": "^1.16.1", "commander": "^14.0.3", "escape-html": "^1.0.3", - "eslint": "^10.3.0", + "eslint": "^10.4.0", "http-parser-js": "^0.5.10", - "jest": "^30.4.1", + "jest": "^30.4.2", "rimraf": "^6.1.3", - "semver": "^7.7.4", + "semver": "^7.8.1", "simple-plist": "^1.4.0", - "ts-jest": "^29.4.9", + "ts-jest": "^29.4.11", "ts-node": "^10.9.2", "typedoc": "^0.28.19", "typescript": "^6.0.3" diff --git a/src/lib/Accessory.spec.ts b/src/lib/Accessory.spec.ts index 7a1d5fe68..cba23244d 100644 --- a/src/lib/Accessory.spec.ts +++ b/src/lib/Accessory.spec.ts @@ -1203,6 +1203,88 @@ describe("Accessory", () => { }); }); + describe("aid.iid format validation in handleHAPConnectionClosed (fix ec93c504)", () => { + test("should skip registered events without a dot separator", () => { + const clearMock = jest.fn(); + const mockConnection = { + getRegisteredEvents: jest.fn().mockReturnValue(new Set([ + "malformed", // no dot — without the fix this triggers parseInt(undefined) + `${aid}.${iids.on}`, // valid + ])), + clearRegisteredEvents: clearMock, + } as unknown as HAPConnection; + + // @ts-expect-error: private access + expect(() => accessory.handleHAPConnectionClosed(mockConnection)).not.toThrow(); + expect(clearMock).toHaveBeenCalled(); + }); + + test("should skip empty registered event strings", () => { + const mockConnection = { + getRegisteredEvents: jest.fn().mockReturnValue(new Set(["", `${aid}.${iids.on}`])), + clearRegisteredEvents: jest.fn(), + } as unknown as HAPConnection; + + // @ts-expect-error: private access + expect(() => accessory.handleHAPConnectionClosed(mockConnection)).not.toThrow(); + }); + }); + + describe("non-null assertions in accessory lookups (fix 03b49f9f)", () => { + test("slow read / timeout warning should not crash for unknown aid.iid in request", async () => { + jest.useFakeTimers({ doNotFake: ["nextTick", "queueMicrotask"] }); + + try { + // @ts-expect-error: private access + accessory.handleGetCharacteristics(connection, { + ids: [ + { aid: 999, iid: 999 }, // unknown accessory — exercises `!accessory` + { aid: aid, iid: 9999 }, // known accessory, unknown iid — exercises `!characteristic` + ], + includeMeta: false, + includeEvent: false, + includeType: false, + includePerms: false, + }, callback); + + // 3s slow read warning fires; without the fix this would throw on + // `accessory!.getCharacteristicByIID(...)` or `characteristic!.displayName`. + jest.advanceTimersByTime(3000); + // 6s later the timeout warning fires and the response is emitted. + jest.advanceTimersByTime(6000); + + await callbackPromise; + expect(callback).toHaveBeenCalledTimes(1); + // unknown ids are skipped, so the response has no characteristics + expect(callback.mock.calls[0][1].characteristics).toEqual([]); + } finally { + jest.useRealTimers(); + } + }); + + test("slow write / timeout warning should not crash for unknown aid.iid in request", async () => { + jest.useFakeTimers({ doNotFake: ["nextTick", "queueMicrotask"] }); + + try { + // @ts-expect-error: private access + accessory.handleSetCharacteristics(connection, { + characteristics: [ + { aid: 999, iid: 999, value: true }, + { aid: aid, iid: 9999, value: true }, + ], + }, callback); + + jest.advanceTimersByTime(3000); + jest.advanceTimersByTime(6000); + + await callbackPromise; + expect(callback).toHaveBeenCalledTimes(1); + } finally { + jest.useRealTimers(); + } + }); + }); + describe("handleSetCharacteristic", () => { let consoleWarnSpy: jest.SpyInstance; diff --git a/src/lib/Advertiser.ts b/src/lib/Advertiser.ts index 0beca2c0d..b54c3bb38 100644 --- a/src/lib/Advertiser.ts +++ b/src/lib/Advertiser.ts @@ -1,10 +1,8 @@ -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// import ciao, { CiaoService, MDNSServerOptions, Responder, ServiceEvent, ServiceTxt, ServiceType } from "@homebridge/ciao"; import { InterfaceName, IPAddress } from "@homebridge/ciao/lib/NetworkManager"; import dbus, { DBusInterface, MessageBus } from "@homebridge/dbus-native"; import assert from "assert"; -import bonjour, { BonjourHAP, BonjourHAPService } from "bonjour-hap"; +import bonjour, { Advertisement, Bonjour } from "bonjour-hap"; import crypto from "crypto"; import createDebug from "debug"; import { EventEmitter } from "events"; @@ -205,8 +203,8 @@ export class BonjourHAPAdvertiser extends EventEmitter implements Advertiser { private readonly setupHash: string; private readonly serviceOptions?: ServiceNetworkOptions; - private bonjour: BonjourHAP; - private advertisement?: BonjourHAPService; + private bonjour: Bonjour; + private advertisement?: Advertisement; private port?: number; private destroyed = false; diff --git a/src/lib/Characteristic.spec.ts b/src/lib/Characteristic.spec.ts index 117788d0f..0556bc5a4 100644 --- a/src/lib/Characteristic.spec.ts +++ b/src/lib/Characteristic.spec.ts @@ -2199,4 +2199,70 @@ describe("Characteristic", () => { }); + describe("undefined string in characteristic error warnings (fix fe8c1b3a)", () => { + test("setValue should emit \"Unknown error\" when validateUserInput throws an object without a message", () => { + const characteristic = createCharacteristic(Formats.STRING); + + // throw a non-Error (no `message` property) — without the fix this + // produced `error?.message + ""` === "undefined" in the warning text + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jest.spyOn(characteristic as any, "validateUserInput").mockImplementation(() => { + throw {}; + }); + + const warnings: { type: CharacteristicWarningType; message: string }[] = []; + characteristic.on(CharacteristicEventTypes.CHARACTERISTIC_WARNING, (type, message) => { + warnings.push({ type, message }); + }); + + characteristic.setValue("anything"); + + const errorWarnings = warnings.filter(w => w.type === CharacteristicWarningType.ERROR_MESSAGE); + expect(errorWarnings.length).toBe(1); + expect(errorWarnings[0].message).toBe("Unknown error"); + expect(errorWarnings[0].message).not.toBe("undefined"); + }); + + test("setValue should preserve a real Error message when validateUserInput throws an Error", () => { + const characteristic = createCharacteristic(Formats.STRING); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jest.spyOn(characteristic as any, "validateUserInput").mockImplementation(() => { + throw new Error("validation failed: bad value"); + }); + + const warnings: { type: CharacteristicWarningType; message: string }[] = []; + characteristic.on(CharacteristicEventTypes.CHARACTERISTIC_WARNING, (type, message) => { + warnings.push({ type, message }); + }); + + characteristic.setValue("anything"); + + const errorWarnings = warnings.filter(w => w.type === CharacteristicWarningType.ERROR_MESSAGE); + expect(errorWarnings.length).toBe(1); + expect(errorWarnings[0].message).toBe("validation failed: bad value"); + }); + + test("updateValue should emit \"Unknown error\" when validateUserInput throws an object without a message", () => { + const characteristic = createCharacteristic(Formats.STRING); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + jest.spyOn(characteristic as any, "validateUserInput").mockImplementation(() => { + throw {}; + }); + + const warnings: { type: CharacteristicWarningType; message: string }[] = []; + characteristic.on(CharacteristicEventTypes.CHARACTERISTIC_WARNING, (type, message) => { + warnings.push({ type, message }); + }); + + characteristic.updateValue("anything"); + + const errorWarnings = warnings.filter(w => w.type === CharacteristicWarningType.ERROR_MESSAGE); + expect(errorWarnings.length).toBe(1); + expect(errorWarnings[0].message).toBe("Unknown error"); + expect(errorWarnings[0].message).not.toBe("undefined"); + }); + }); + }); diff --git a/src/lib/HAPServer.spec.ts b/src/lib/HAPServer.spec.ts index e705ac426..f2e484097 100644 --- a/src/lib/HAPServer.spec.ts +++ b/src/lib/HAPServer.spec.ts @@ -1,8 +1,9 @@ import axios, { AxiosError, AxiosResponse } from "axios"; import crypto from "crypto"; +import createDebug from "debug"; import { Agent } from "http"; import tweetnacl from "tweetnacl"; -import { PairingStates, TLVValues } from "../internal-types"; +import { PairingStates, PairMethods, TLVValues } from "../internal-types"; import { HAPHTTPClient } from "../test-utils/HAPHTTPClient"; import { PairSetupClient } from "../test-utils/PairSetupClient"; import { PairVerifyClient } from "../test-utils/PairVerifyClient"; @@ -30,6 +31,7 @@ import { import { AccessoryInfo, PairingInformation, PermissionTypes } from "./model/AccessoryInfo"; import { Service } from "./Service"; import { HAPConnection, HAPEncryption } from "./util/eventedhttp"; +import * as hapCrypto from "./util/hapCrypto"; import { awaitEventOnce, PromiseTimeout } from "./util/promise-utils"; import * as tlv from "./util/tlv"; @@ -232,6 +234,489 @@ describe("HAPServer", () => { expect(objectsM4[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); }); + describe("encrypted data length validation (fix 0719059b)", () => { + test("/pair-setup M5 should reject when ENCRYPTED_DATA is missing", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + + // advance the connection state to M4 via real SRP M1 + M3 + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + pairSetup.parseM4(responseM3.data, M3); + + // craft an M5 with no ENCRYPTED_DATA TLV + const response = await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode(TLVValues.STATE, PairingStates.M5), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + + test("/pair-setup M5 should reject when ENCRYPTED_DATA is shorter than the auth tag (16 bytes)", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + pairSetup.parseM4(responseM3.data, M3); + + // 8 bytes is below the 16-byte minimum; without the guard this would underflow Buffer.alloc() + const response = await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode( + TLVValues.STATE, PairingStates.M5, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(8), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + + test("/pair-verify M3 should reject when ENCRYPTED_DATA is missing", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + // advance connection state to M2 via M1 + const responseM1 = await pairVerify.sendM1(); + pairVerify.parseM2(responseM1.data, serverInfoPaired); + + const response = await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode(TLVValues.STATE, PairingStates.M3), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + + test("/pair-verify M3 should reject when ENCRYPTED_DATA is shorter than the auth tag (16 bytes)", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + const responseM1 = await pairVerify.sendM1(); + pairVerify.parseM2(responseM1.data, serverInfoPaired); + + const response = await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode( + TLVValues.STATE, PairingStates.M3, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(15), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.AUTHENTICATION); + }); + }); + + describe("constant-time pincode comparison (fix 12aea013)", () => { + test("PUT /characteristics with no authorization header should be rejected", async () => { + server = new HAPServer(accessoryInfoUnpaired); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + try { + await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + expect(error.response?.data).toEqual({ status: HAPStatus.INSUFFICIENT_PRIVILEGES }); + } + }); + + test("PUT /characteristics with wrong-length authorization should not crash timingSafeEqual", async () => { + server = new HAPServer(accessoryInfoUnpaired); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + // 5 bytes — different length to the 11-byte pincode. Without the + // length guard added in 12aea013, crypto.timingSafeEqual throws + // RangeError and the connection would be killed. + try { + await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent, headers: { authorization: "short" } }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + } + }); + + test("PUT /characteristics with same-length wrong pincode should be rejected", async () => { + // use a pincode without leading whitespace — node's HTTP parser trims + // header values, so the fixture's " 031-45-154" can't round-trip and + // any wrong value with the same leading space hits the wrong-length + // branch on the server, not the constant-time same-length comparison. + const info = AccessoryInfo.create(serverUsername); + // @ts-expect-error: private access + info.setupID = Accessory._generateSetupID(); + info.displayName = "Outlet"; + info.category = 7; + info.pincode = "031-45-154"; + + server = new HAPServer(info); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + const wrongPincode = "999-99-999"; // same length as "031-45-154", different content + try { + await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent, headers: { authorization: wrongPincode } }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + } + }); + + test("PUT /characteristics with correct pincode should be authorized", async () => { + // use a pincode without leading whitespace — HTTP header values get trimmed + // by node's parser so the global test fixture's " 031-45-154" can't round-trip. + const info = AccessoryInfo.create(serverUsername); + // @ts-expect-error: private access + info.setupID = Accessory._generateSetupID(); + info.displayName = "Outlet"; + info.category = 7; + info.pincode = "031-45-154"; + + server = new HAPServer(info); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + server.on(HAPServerEventTypes.SET_CHARACTERISTICS, (connection, writeRequest, callback) => { + callback(undefined, { characteristics: writeRequest.characteristics.map(c => ({ aid: c.aid, iid: c.iid, status: HAPStatus.SUCCESS })) }); + }); + + const response = await axios.put( + `http://localhost:${port}/characteristics`, + { characteristics: [{ aid: 1, iid: 9, value: true }] }, + { httpAgent, headers: { authorization: info.pincode } }, + ); + // a successful insecure PUT returns 204 NO_CONTENT (or 200 if there's a response body) + expect(response.status).toBeGreaterThanOrEqual(200); + expect(response.status).toBeLessThan(300); + }); + + test("POST /resource with wrong-length authorization should not crash timingSafeEqual", async () => { + server = new HAPServer(accessoryInfoUnpaired); + server.allowInsecureRequest = true; + const [port] = await bindServer(server); + + try { + await axios.post( + `http://localhost:${port}/resource`, + { "resource-type": "image", "image-width": 100, "image-height": 100 }, + { httpAgent, headers: { authorization: "short" } }, + ); + fail("Expected CONNECTION_AUTHORIZATION_REQUIRED response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPPairingHTTPCode.CONNECTION_AUTHORIZATION_REQUIRED); + expect(error.response?.data).toEqual({ status: HAPStatus.INSUFFICIENT_PRIVILEGES }); + } + }); + }); + + describe("error argument in pairing debug logs (fix f12ed233)", () => { + let originalEnable: string; + let originalLog: (...args: unknown[]) => void; + let captured: unknown[][]; + + beforeEach(() => { + captured = []; + // createDebug.disable() returns the namespaces it just disabled, so we + // can faithfully restore them via createDebug.enable() in afterEach. + originalEnable = createDebug.disable(); + // route all enabled debug output through our capture function + // eslint-disable-next-line @typescript-eslint/no-explicit-any + originalLog = (createDebug as any).log; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createDebug as any).log = function(this: { namespace: string }, ...args: unknown[]) { + captured.push([this.namespace, ...args]); + }; + createDebug.enable("HAP-NodeJS:HAPServer"); + }); + + afterEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (createDebug as any).log = originalLog; + createDebug.disable(); + if (originalEnable) { + createDebug.enable(originalEnable); + } + }); + + test("M5 decrypt failure should pass the error to the debug logger", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + pairSetup.parseM4(responseM3.data, M3); + + // craft an M5 with bogus ciphertext+tag (passes the >=16 length check + // from 0719059b but fails MAC verification) + await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode( + TLVValues.STATE, PairingStates.M5, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(48), // 32-byte payload + 16-byte tag, all zeros + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + // captured rows: [namespace, formatString, ...formatArgs] + const m5Lines = captured.filter(line => typeof line[1] === "string" + && (line[1] as string).includes("Error while decrypting and verifying M5 subTlv")); + expect(m5Lines.length).toBeGreaterThan(0); + + // the format string contains two %s — the first is the username, the + // second is the error. Without the fix only the username was passed. + const line = m5Lines[0]; + expect(line[2]).toBe(accessoryInfoUnpaired.username); + // line[3] should be the error caught from decipher.final() — the + // exact constructor varies by Node version / openssl binding, so + // just assert it's a thrown error-like object with a message. + expect(line[3]).toBeDefined(); + expect((line[3] as Error).message).toEqual(expect.any(String)); + }); + + test("pair-verify M3 decrypt failure should pass the error to the debug logger", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + const responseM1 = await pairVerify.sendM1(); + pairVerify.parseM2(responseM1.data, serverInfoPaired); + + await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode( + TLVValues.STATE, PairingStates.M3, + TLVValues.ENCRYPTED_DATA, Buffer.alloc(48), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const m3Lines = captured.filter(line => typeof line[1] === "string" + && (line[1] as string).includes("M3: Failed to decrypt and/or verify")); + expect(m3Lines.length).toBeGreaterThan(0); + const line = m3Lines[0]; + expect(line[2]).toBe(accessoryInfoPaired.username); + // line[3] should be the error caught from decipher.final() — the + // exact constructor varies by Node version / openssl binding, so + // just assert it's a thrown error-like object with a message. + expect(line[3]).toBeDefined(); + expect((line[3] as Error).message).toEqual(expect.any(String)); + }); + }); + + describe("M1 reset prevention (fix d4c81be0)", () => { + test("/pair-setup should reject a second M1 on a connection with in-progress state", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + + // first M1 succeeds → connection state advances to M2 + const firstResponse = await pairSetup.sendM1(); + const firstObjects = tlv.decode(firstResponse.data); + expect(firstObjects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(firstObjects[TLVValues.ERROR_CODE]).toBeUndefined(); + + // second M1 on the same connection — without the guard this would + // restart pair-setup and overwrite the in-progress SRP server + try { + await pairSetup.sendM1(); + fail("Expected BAD_REQUEST response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST); + const objects = tlv.decode(error.response?.data); + // sequence + 1 = M2; UNKNOWN error + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + } + }); + + test("/pair-setup should still accept M1 on a fresh connection after a previous setup completed", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + // first connection: do an M1 and abort + const firstAgent = new Agent({ keepAlive: true }); + const firstClient = new PairSetupClient(port, firstAgent); + await firstClient.sendM1(); + firstAgent.destroy(); + + // second connection: a fresh M1 should still succeed (no state pollution) + const secondAgent = new Agent({ keepAlive: true }); + try { + const secondClient = new PairSetupClient(port, secondAgent); + const response = await secondClient.sendM1(); + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE]).toBeUndefined(); + } finally { + secondAgent.destroy(); + } + }); + }); + + describe("SEQUENCE_NUM check in pair handlers (fix ebe2ec67)", () => { + test("/pair-setup should reject when SEQUENCE_NUM TLV is missing", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + // a TLV containing only METHOD (no SEQUENCE_NUM/STATE) — without the + // guard this would crash on `tlvData[SEQUENCE_NUM][0]` of `undefined`. + try { + await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode(TLVValues.METHOD, PairMethods.PAIR_SETUP), + { httpAgent, responseType: "arraybuffer" }, + ); + fail("Expected BAD_REQUEST response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST); + const objects = tlv.decode(error.response?.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + } + }); + + test("/pair-verify should reject when SEQUENCE_NUM TLV is missing", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + try { + await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode(TLVValues.PUBLIC_KEY, Buffer.alloc(32)), + { httpAgent, responseType: "arraybuffer" }, + ); + fail("Expected BAD_REQUEST response"); + } catch (error) { + expect(error).toBeInstanceOf(AxiosError); + expect(error.response?.status).toBe(HAPHTTPCode.BAD_REQUEST); + const objects = tlv.decode(error.response?.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + } + }); + }); + + describe("required TLV fields validation in pairing handlers (fix 58c24b92)", () => { + test("/pair-setup M5 should reject when decrypted payload is missing IDENTIFIER", async () => { + server = new HAPServer(accessoryInfoUnpaired); + const [port] = await bindServer(server); + + const pairSetup = new PairSetupClient(port, httpAgent); + + const responseM1 = await pairSetup.sendM1(); + const M2 = pairSetup.parseM2(responseM1.data); + const M3 = await pairSetup.prepareM3(M2, accessoryInfoUnpaired.pincode); + const responseM3 = await pairSetup.sendM3(M3); + const M4 = pairSetup.parseM4(responseM3.data, M3); + + // build a sub-TLV missing IDENTIFIER (only PUBLIC_KEY + SIGNATURE) + const malformedSubTLV = tlv.encode( + TLVValues.PUBLIC_KEY, clientInfo.publicKey, + TLVValues.SIGNATURE, Buffer.alloc(64), + ); + + const sessionKey = hapCrypto.HKDF( + "sha512", + Buffer.from("Pair-Setup-Encrypt-Salt"), + M4.sharedSecret, + Buffer.from("Pair-Setup-Encrypt-Info"), + 32, + ); + const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal( + sessionKey, Buffer.from("PS-Msg05"), null, malformedSubTLV, + ); + + const response = await axios.post( + `http://localhost:${port}/pair-setup`, + tlv.encode( + TLVValues.STATE, PairingStates.M5, + TLVValues.ENCRYPTED_DATA, Buffer.concat([encrypted.ciphertext, encrypted.authTag]), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("/pair-verify M3 should reject when decrypted payload is missing PROOF", async () => { + server = new HAPServer(accessoryInfoPaired); + const [port] = await bindServer(server); + + const pairVerify = new PairVerifyClient(port, httpAgent); + const responseM1 = await pairVerify.sendM1(); + const M2 = pairVerify.parseM2(responseM1.data, serverInfoPaired); + + // build a sub-TLV missing PROOF/SIGNATURE (only IDENTIFIER) + const malformedSubTLV = tlv.encode( + TLVValues.IDENTIFIER, Buffer.from(clientInfo.username), + ); + + const encrypted = hapCrypto.chacha20_poly1305_encryptAndSeal( + M2.sessionKey, Buffer.from("PV-Msg03"), null, malformedSubTLV, + ); + + const response = await axios.post( + `http://localhost:${port}/pair-verify`, + tlv.encode( + TLVValues.STATE, PairingStates.M3, + TLVValues.ENCRYPTED_DATA, Buffer.concat([encrypted.ciphertext, encrypted.authTag]), + ), + { httpAgent, responseType: "arraybuffer" }, + ); + + const objects = tlv.decode(response.data); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M4); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + }); + describe("tests with paired and pair-verified connection", () => { let port: number; let address: string; @@ -304,6 +789,88 @@ describe("HAPServer", () => { expect(response).toEqual(list); }); + describe("/pairings required TLV fields (fix 58c24b92)", () => { + test("should reject when METHOD is missing", async () => { + const malformed = tlv.encode(TLVValues.STATE, PairingStates.M1); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + expect(httpResponse.statusCode).toEqual(HAPPairingHTTPCode.OK); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("should reject when STATE is missing", async () => { + const malformed = tlv.encode(TLVValues.METHOD, PairMethods.LIST_PAIRINGS); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + expect(httpResponse.statusCode).toEqual(HAPPairingHTTPCode.OK); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("ADD_PAIRING should reject when IDENTIFIER is missing", async () => { + const malformed = tlv.encode( + TLVValues.METHOD, PairMethods.ADD_PAIRING, + TLVValues.STATE, PairingStates.M1, + TLVValues.PUBLIC_KEY, crypto.randomBytes(32), + TLVValues.PERMISSIONS, PermissionTypes.ADMIN, + ); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("ADD_PAIRING should reject when PUBLIC_KEY is missing", async () => { + const malformed = tlv.encode( + TLVValues.METHOD, PairMethods.ADD_PAIRING, + TLVValues.STATE, PairingStates.M1, + TLVValues.IDENTIFIER, thirdUsername, + TLVValues.PERMISSIONS, PermissionTypes.ADMIN, + ); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + + test("REMOVE_PAIRING should reject when IDENTIFIER is missing", async () => { + const malformed = tlv.encode( + TLVValues.METHOD, PairMethods.REMOVE_PAIRING, + TLVValues.STATE, PairingStates.M1, + ); + const httpResponse = await client.writeHTTPRequest("POST", "/pairings", malformed, "application/pairing+tlv8"); + const objects = tlv.decode(httpResponse.body); + expect(objects[TLVValues.STATE].readUInt8(0)).toEqual(PairingStates.M2); + expect(objects[TLVValues.ERROR_CODE].readUInt8(0)).toEqual(TLVErrorCode.UNKNOWN); + }); + }); + + describe("GET /characteristics aid.iid format validation (fix ec93c504)", () => { + test.each([ + ["non-numeric aid", "abc.5"], + ["non-numeric iid", "1.xyz"], + ["missing dot", "1"], + ["too many dots", "1.2.3"], + ["empty parts", "."], + ["empty aid", ".5"], + ["empty iid", "1."], + ["whitespace", "1. 5"], + ])("should reject id=%s (%s) with INVALID_VALUE_IN_REQUEST", async (_label, idValue) => { + const httpResponse = await client.writeHTTPRequest("GET", `/characteristics?id=${encodeURIComponent(idValue)}`); + expect(httpResponse.statusCode).toBe(HAPHTTPCode.BAD_REQUEST); + const body = JSON.parse(httpResponse.body.toString()); + expect(body.status).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + + test("should reject when one entry in a comma-separated list is malformed", async () => { + const httpResponse = await client.writeHTTPRequest("GET", "/characteristics?id=1.5,abc.def"); + expect(httpResponse.statusCode).toBe(HAPHTTPCode.BAD_REQUEST); + const body = JSON.parse(httpResponse.body.toString()); + expect(body.status).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + }); + test("test /accessories", async () => { const accessoryResponse: AccessoriesResponse = { accessories: [{ diff --git a/src/lib/camera/RTPStreamManagement.spec.ts b/src/lib/camera/RTPStreamManagement.spec.ts new file mode 100644 index 000000000..8b143ec9f --- /dev/null +++ b/src/lib/camera/RTPStreamManagement.spec.ts @@ -0,0 +1,268 @@ +import { + CameraController, + CameraControllerOptions, + CameraRecordingDelegate, + CameraStreamingDelegate, + PrepareStreamCallback, + SnapshotRequestCallback, + StreamRequestCallback, +} from "../controller"; +import { + AudioBitrate, + AudioRecordingCodecType, + AudioRecordingSamplerate, + AudioStreamingCodecType, + AudioStreamingSamplerate, + CameraRecordingConfiguration, + CameraRecordingOptions, + H264Level, + H264Profile, + MediaContainerType, + PrepareStreamRequest, + RecordingPacket, + SnapshotRequest, + StreamingRequest, + VideoCodecType, +} from "../camera"; +import { HDSProtocolSpecificErrorReason } from "../datastream"; +import "../definitions"; +import { HAPStatus } from "../HAPServer"; +import { HAPConnection } from "../util/eventedhttp"; +import * as tlv from "../util/tlv"; +import * as uuid from "../util/uuid"; +import RTPProxy from "./RTPProxy"; +import { CameraStreamingOptions, RTPStreamManagement, SRTPCryptoSuites } from "./RTPStreamManagement"; + +class MockDelegate implements CameraStreamingDelegate, CameraRecordingDelegate { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handleSnapshotRequest(request: SnapshotRequest, callback: SnapshotRequestCallback): void { + throw Error("Unsupported!"); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handleStreamRequest(request: StreamingRequest, callback: StreamRequestCallback): void { + throw Error("Unsupported!"); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + prepareStream(request: PrepareStreamRequest, callback: PrepareStreamCallback): void { + throw Error("Unsupported!"); + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async *handleRecordingStreamRequest(streamId: number): AsyncGenerator { + yield { data: Buffer.alloc(64, 0), isLast: true }; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + closeRecordingStream(streamId: number, reason: HDSProtocolSpecificErrorReason): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateRecordingActive(active: boolean): void {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateRecordingConfiguration(configuration: CameraRecordingConfiguration | undefined): void {} +} + +const baseStreamingOptions: CameraStreamingOptions = { + supportedCryptoSuites: [SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80], + video: { + codec: { + profiles: [H264Profile.BASELINE, H264Profile.MAIN, H264Profile.HIGH], + levels: [H264Level.LEVEL3_1, H264Level.LEVEL3_2, H264Level.LEVEL4_0], + }, + resolutions: [ + [1920, 1080, 30], + [1280, 720, 30], + [320, 240, 15], + ], + }, + audio: { + twoWayAudio: true, + codecs: [{ + type: AudioStreamingCodecType.AAC_ELD, + samplerate: AudioStreamingSamplerate.KHZ_24, + }], + }, +}; + +const baseRecordingOptions: CameraRecordingOptions = { + prebufferLength: 4000, + mediaContainerConfiguration: [{ + type: MediaContainerType.FRAGMENTED_MP4, + fragmentLength: 8000, + }], + video: { + type: VideoCodecType.H264, + parameters: baseStreamingOptions.video.codec, + resolutions: baseStreamingOptions.video.resolutions, + }, + audio: { + codecs: [{ + type: AudioRecordingCodecType.AAC_ELD, + audioChannels: 1, + bitrateMode: AudioBitrate.VARIABLE, + samplerate: AudioRecordingSamplerate.KHZ_48, + }], + }, +}; + +function buildControllerOptions(streamingOptions: CameraStreamingOptions): CameraControllerOptions { + const delegate = new MockDelegate(); + return { + cameraStreamCount: 2, + delegate, + streamingOptions, + recording: { + options: baseRecordingOptions, + delegate, + }, + sensors: { motion: true, occupancy: true }, + }; +} + +// matches the private const enums in RTPStreamManagement.ts +const SetupEndpointsTypes = { + SESSION_ID: 0x01, + CONTROLLER_ADDRESS: 0x03, + VIDEO_SRTP_PARAMETERS: 0x04, + AUDIO_SRTP_PARAMETERS: 0x05, +} as const; +const AddressTypes = { + ADDRESS_VERSION: 0x01, + ADDRESS: 0x02, + VIDEO_RTP_PORT: 0x03, + AUDIO_RTP_PORT: 0x04, +} as const; +const SRTPParametersTypes = { + SRTP_CRYPTO_SUITE: 0x01, + MASTER_KEY: 0x02, + MASTER_SALT: 0x03, +} as const; + +function buildSetupEndpointsTlv(): string { + const addressTlv = tlv.encode( + AddressTypes.ADDRESS_VERSION, 0x00, // IPV4 + AddressTypes.ADDRESS, "127.0.0.1", + AddressTypes.VIDEO_RTP_PORT, tlv.writeUInt16(5000), + AddressTypes.AUDIO_RTP_PORT, tlv.writeUInt16(5001), + ); + const srtpTlv = tlv.encode( + SRTPParametersTypes.SRTP_CRYPTO_SUITE, SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80, + SRTPParametersTypes.MASTER_KEY, Buffer.alloc(16), + SRTPParametersTypes.MASTER_SALT, Buffer.alloc(14), + ); + const payload = tlv.encode( + SetupEndpointsTypes.SESSION_ID, uuid.write("11111111-1111-1111-1111-111111111111"), + SetupEndpointsTypes.CONTROLLER_ADDRESS, addressTlv, + SetupEndpointsTypes.VIDEO_SRTP_PARAMETERS, srtpTlv, + SetupEndpointsTypes.AUDIO_SRTP_PARAMETERS, srtpTlv, + ); + return payload.toString("base64"); +} + +function buildMockConnection(): HAPConnection { + return { + localAddress: "127.0.0.1", + getLocalAddress: jest.fn().mockReturnValue("127.0.0.1"), + setMaxListeners: jest.fn(), + getMaxListeners: jest.fn().mockReturnValue(10), + on: jest.fn(), + removeListener: jest.fn(), + } as unknown as HAPConnection; +} + +describe("RTPStreamManagement", () => { + describe("_handleStartStream malformed TLV (fix 34056583)", () => { + let controller: CameraController; + + beforeEach(() => { + controller = new CameraController(buildControllerOptions(baseStreamingOptions)); + controller.constructServices(); + controller.configureServices(); + }); + + afterEach(() => { + controller.handleFactoryReset(); + }); + + test("empty video configuration should call callback with INVALID_VALUE_IN_REQUEST", () => { + const rtp = controller.streamManagements[0] as RTPStreamManagement; + const callback = jest.fn(); + + // empty video/audio config — every indexed buffer access (e.g. + // `videoParameters[VideoCodecParametersTypes.PROFILE_ID][0]`) would + // throw without the try/catch from 34056583. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (rtp as any)._handleStartStream({}, {}, callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls[0][0]).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + + test("video configuration missing CODEC_PARAMETERS should not throw", () => { + const rtp = controller.streamManagements[0] as RTPStreamManagement; + const callback = jest.fn(); + + // SelectedVideoParametersTypes.CODEC_TYPE = 0x01 — provide that but + // omit CODEC_PARAMETERS / ATTRIBUTES / RTP_PARAMETERS so the inner + // `tlv.decode(undefined)` would have crashed without the guard. + const videoConfig = { 0x01: Buffer.from([0x00]) }; + + expect(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (rtp as any)._handleStartStream(videoConfig, {}, callback); + }).not.toThrow(); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback.mock.calls[0][0]).toBe(HAPStatus.INVALID_VALUE_IN_REQUEST); + }); + }); + + describe("handleSetupEndpoints proxy rejection (fix 0ce74c4e)", () => { + let controller: CameraController; + let setupSpy: jest.SpyInstance; + + beforeEach(() => { + const proxyOptions: CameraStreamingOptions = { + ...baseStreamingOptions, + proxy: true, + }; + controller = new CameraController(buildControllerOptions(proxyOptions)); + controller.constructServices(); + controller.configureServices(); + }); + + afterEach(() => { + setupSpy?.mockRestore(); + controller.handleFactoryReset(); + }); + + test("should fail gracefully when RTPProxy.setup rejects", async () => { + setupSpy = jest.spyOn(RTPProxy.prototype, "setup") + .mockImplementation(() => Promise.reject(new Error("socket creation failed"))); + + const rtp = controller.streamManagements[0] as RTPStreamManagement; + const error = await new Promise((resolve) => { + // @ts-expect-error: private method access + rtp.handleSetupEndpoints(buildSetupEndpointsTlv(), (err) => { + resolve(err as Error | undefined); + }, buildMockConnection()); + }); + + expect(error).toBeInstanceOf(Error); + expect(error!.message).toBe("socket creation failed"); + }); + + test("should set setupEndpointsResponse to ERROR status when proxy setup rejects", async () => { + setupSpy = jest.spyOn(RTPProxy.prototype, "setup") + .mockImplementation(() => Promise.reject(new Error("bind failed"))); + + const rtp = controller.streamManagements[0] as RTPStreamManagement; + await new Promise((resolve) => { + // @ts-expect-error: private method access + rtp.handleSetupEndpoints(buildSetupEndpointsTlv(), () => resolve(), buildMockConnection()); + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response = (rtp as any).setupEndpointsResponse as string; + const decoded = tlv.decode(Buffer.from(response, "base64")); + // SetupEndpointsResponseTypes.STATUS = 0x02; SetupEndpointsStatus.ERROR = 0x02 + expect(decoded[0x02].readUInt8(0)).toBe(0x02); + }); + }); +});