Repository navigation
Add validation of perms array, and skip bridging accessories with malformed data - #1129
Conversation
Coverage Report for CI Build 32147210020Warning No base build found for commit Coverage: 66.735%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats💛 - Coveralls |
bwp91
left a comment
There was a problem hiding this comment.
The throw-on-add / skip-on-publish split is the right shape now. A few things to change before this goes in:
VALID_PERMISSIONShardcodes the wire strings, so it is no longer tied to thePermsenum. Add a permission to the enum later and this will reject it as invalid, with nothing to catch it at compile time. Moving the enum itself intoutil/characteristic-permissions.tsand re-exporting it fromCharacteristic.tswould fix that and the circular import you are working around.validatePerms([])returnstrue—everyon an empty array is vacuously true. Both callers happen to check the length separately so nothing is broken today, but the check belongs inside the function.- Nothing tests that
publish()callsremoveBridgedAccessoriesWithInvalidPermissions(). The test calls the private method directly, so deleting that line frompublish()leaves the suite green. Please add a case that goes throughpublish()itself. validateAccessoryhas lost its doc comment — the existing block has been repurposed forvalidateCharacteristicPermissions.- Changelog: the heading should be
## v2.2.3 (Pending Release)to match the rest of the file, and the bullet should be the commit subject, lowercase and without the full stop. - The commit title
Fix for #1128will close that issue on merge. Please unlink it — malformed perms have not been shown to be the cause there, and I want to keep that one open.
… and the accessories serving boundary, with per-accessory quarantine of non-compliant bridged accessories.
|
I've taken this the last mile and pushed two commits onto the branch. The diagnosis here was on-point, and the setProps validation was the right instinct...what I've changed is where the enforcement lives. What moved and why. The add-time throw and the publish-time removal sweep are gone. In their place, the serving boundary now does the enforcement: both toHAP and internalHAPRepresentation draw their bridged accessories through a single filter that excludes any accessory whose permissions HomeKit would reject. Three reasons for the move:
Because both representation builders share the one filter, the served database and the hashed configuration can't disagree - a quarantine transition bumps c#, controllers re-read, and everyone converges. The transition also emits a characteristic warning naming the accessory, service, characteristic, and the offending array, once, so the responsible plugin is identifiable from the log. One deliberate attribute: a quarantined accessory's aid remains reachable for reads and events until the controller re-reads after the c# bump. The setProps throw stays, consolidated into a single predicate (isValidPerms) that now lives beside the Perms enum and references its members rather than restating the wire strings - which also picks up @bwp91's points about the enum-tied set and the vacuous empty check. The validateAccessory doc comment is restored, and the new tests drive the real /accessories route rather than the private helpers. I've verified each new test fails when the behavior it pins is broken. 901 tests passing, lint and docs clean. Second commit. While in there I fixed a latent bug: validateAccessory's recursion guard read this.bridged, which is false on a bridge, so a bridge never actually validated its bridged accessories despite the doc saying it does. Bridges validate their children at publish now. On #1128 - there's also another interesting bit...the captured dump carries a second compliance defect besides the perms: an accessory name with a trailing space. @NorthernMan54, since you have the replay harness up, a bisect would settle causation - the dump minus the three weather accessories, then minus only the trailing-space name. If the name turns out to matter to tvOS 26 too, promoting name validation into this same quarantine mechanism is a natural follow-up. Nice work! |
There was a problem hiding this comment.
Pull request overview
Adds characteristic permission validation and quarantines malformed bridged accessories so valid accessories remain available.
Changes:
- Adds runtime permission validation.
- Excludes and reports invalid bridged accessories.
- Adds tests and release notes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/lib/Characteristic.ts |
Adds permission validation. |
src/lib/Characteristic.spec.ts |
Tests validation behavior. |
src/lib/Accessory.ts |
Implements publication checks and quarantine. |
src/lib/Accessory.spec.ts |
Tests blocking and recovery behavior. |
CHANGELOG.md |
Documents the fixes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ostics render any value without throwing.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/lib/Characteristic.ts:1879
- The truthy guard above means falsy malformed values such as
"",false, or0never reach this validator;setPropssilently keeps the old permissions instead of rejecting the invalid input. Preserve the existingnull/undefinedno-op behavior for this required property, but validate every other supplied value (and add a falsy case to the table test).
if (!isValidPerms(props.perms)) {
throw new Error(`characteristic '${this.displayName}' (${this.UUID}) contains invalid permissions: ${describePerms(props.perms)}`);
@hjdhjd During testing we tested with just minus the three weather accessories and it past compliance. The names also include an Emoji and it past compliance as well as the trailing space issue. |
from claude:This is good work from both of you, and the diagnosis in particular — going from a I agree with the move to the serving boundary. Catching a direct The One consequence of fixing that, worth deciding on deliberately: every bridged accessory now gets the AccessoryInformation checks for the first time. Those are The one thing I'd want resolved before this goes inThe reasoning for dropping the
So the plugin that started all this — the Eve Air Pressure characteristic carrying That is the opposite of what the quarantine mechanism is for. My preference is that Two decisions I'd like your view on1. Should
2. Should It walks every characteristic of every bridged accessory on each
On the trailing space and the emojiThat looks settled by the testing above: removing only the three weather accessories passed compliance, and the names with an emoji and a trailing space passed too. So the perms array is the cause, and name validation is not indicated. Worth saying so explicitly on #1128 so nobody reopens that line later. |
|
As the original reporter whose logs and The accessory named: is not related to I corrected the device name by removing the trailing space: However, the Homebridge accessory continued to expose the previous name with the trailing space, so the change made on the device/Home Connect side did not propagate to the existing accessory. Just mentioning this to avoid associating that particular warning with Weather Plus; the malformed |
Thanks - I didn't think it would ultimately be a cause...but it is an issue that should be addressed on the user front to ensure you don't end up with any odd behavior downstream (Siri, etc). Thanks again! |
|
Thank you. I opened a dedicated issue for the relevant plugin: thoukydides/homebridge-homeconnect#400 |
…ic warning channel with the value retained for serving-boundary quarantine, and the permission helpers are internal to the API.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/lib/Accessory.ts:995
- The boolean suppresses diagnostics for a new violation while the accessory remains quarantined. For example, if two characteristics are invalid, fixing the first leaves the accessory excluded because the second is now returned, but no warning identifies that second characteristic. Track the current offending service/characteristic (rather than only quarantine state) and emit again when that identity changes; configuration updates should still occur only when entering or leaving quarantine.
if (violation) {
if (!accessory.permsQuarantined) {
accessory.permsQuarantined = true;
accessory.sendCharacteristicWarning(
I flipped the One correction to the analysis above: the cached-restore path does wrap The publish-time throw stays. Both Homebridge call sites wrap Neither helper becomes public API. This is a bugfix - we aren't growing the API surface here. Both are
The |
…ridge (#1129) Co-Authored-By: hjdhjd <hjdhjd@users.noreply.github.com> Co-Authored-By: Northern Man <19808920+NorthernMan54@users.noreply.github.com>
…ridge (#1129) Co-Authored-By: hjdhjd <hjdhjd@users.noreply.github.com> Co-Authored-By: Northern Man <19808920+NorthernMan54@users.noreply.github.com>
…ridge (#1129) Co-Authored-By: hjdhjd <hjdhjd@users.noreply.github.com> Co-Authored-By: Northern Man <19808920+NorthernMan54@users.noreply.github.com>
…ridge (#1129) Co-Authored-By: hjdhjd <hjdhjd@users.noreply.github.com> Co-Authored-By: Northern Man <19808920+NorthernMan54@users.noreply.github.com>
…ridge (#1129) Co-Authored-By: hjdhjd <hjdhjd@users.noreply.github.com> Co-Authored-By: Northern Man <19808920+NorthernMan54@users.noreply.github.com>
After digging through a supplied /accessories dump, we found that a Homebridge plugin can place null values in a characteristic’s perms array.
If this malformed data reaches HomeKit, the entire bridge may show No Response. When adding a new bridge, HomeKit instead reports Accessory Out of Compliance.
The fix validates every perms array before publication. If an accessory fails validation, HAP-NodeJS logs the plugin, accessory, service, and characteristic responsible, then blocks that accessory so the valid accessories can continue working.
Sample logs: