Skip to content

Bump to 0.27.3: the round that found its own gates were the problem - #200

Merged
Timdebruijn merged 1 commit into
mainfrom
chore/bump-0.27.3
Aug 29, 2026
Merged

Bump to 0.27.3: the round that found its own gates were the problem#200
Timdebruijn merged 1 commit into
mainfrom
chore/bump-0.27.3

Conversation

@Timdebruijn

Copy link
Copy Markdown
Owner

Version bump for the four merged PRs: #196, #197, #198, #199.

A patch, following 0.27.1 and 0.27.2 — a review round with no new capability.

⚠ Two behaviour changes, both in /api/v1/diagnostics

previous_uptime_ms is now always a whole number of seconds. Still milliseconds, same accuracy — it was only ever sampled once per second — but the low digits are zeros. The uint32 behind it wrapped at 49.7 days, so the long-lived bridges the field exists to describe were exactly the ones it misreported.

Every device reports one cold start on this update, boot count restarting at 1. The fix above changed a field's unit without changing the layout, so a record written by 0.27.2 still checksummed and read back a thousandfold too large. A schema tag folded into the CRC makes it fail validation instead. Unknown is the honest answer — those bytes cannot be interpreted.

What is in it

  • A stored XSS in the dashboard, reachable from a device serial number on the RS485 bus
  • Tests for seventeen guards that nothing protected — two of which move relays or confirm a setpoint that never arrived
  • A release gate that let vtest, v-wip and a bare v publish with no version check
  • Three dependency ranges pinned, because a caret is not a pin and this builds an OTA image

Verification

1043 native cases · check_layering.sh · check_version.sh --self-test · waveshare-rs485-can builds clean · check_version.sh v0.27.3 agrees with src/main.cpp.

A review round, like 0.27.1 and 0.27.2 -- no new capability, so a patch. What it fixes
is mostly the machinery that was supposed to be catching things.

Mutation testing across thirteen suites, 114 mutations, found seventeen guards that no
test protected. Two mattered: patternFor() refusing DRM mode "none" (without it, POST
/api/v1/drm/set?mode=none energises every unassigned relay on a board wired to DRM
inputs), and writeSingleRegister's echo validation, which had no test at all while two
drivers call it -- a setpoint that never arrived would have been recorded as delivered.

A stored XSS in the dashboard: inline handlers interpolated a device id built from the
serial number an inverter reports over RS485, filtered only to printable ASCII, which
includes the apostrophe. esc() does not help there -- the browser HTML-decodes an
attribute before compiling the handler. All nine handlers now pass their value as data-*.

The release gate recognised a tag as /^v[0-9]/ while release.yml triggers on "v*", so
vtest, v-wip and a bare v each built and published a release with no version check at
all. It keys off the ref type now, and has a self-test that runs on every push.

Three dependency ranges became pins. This builds an OTA image; the version that shipped
has to be reconstructible from the tag, and a caret is not a pin.

TWO BEHAVIOUR CHANGES, both in the diagnostics payload:

  previous_uptime_ms is still milliseconds and is now always a whole number of seconds.
  Accuracy is unchanged -- it was only ever sampled once per second -- but the low digits
  are zeros now. The uint32 behind it was wrapping at 49.7 days, so the long-lived
  bridges the field exists to describe were exactly the ones it misreported.

  Every device reports ONE cold start on this update, with its boot count restarting at
  1. The fix above changed a field's unit without changing the layout, so an old record
  still checksummed and read back a thousandfold too large; a schema tag folded into the
  CRC makes it fail validation instead. Unknown is the honest answer -- those bytes
  cannot be interpreted.

Three of the four gates in this release were themselves broken, and each was found by
review rather than by running them: a dashboard check that could not fail after the
attributes it looked for were renamed, a handler check with four ways round it, and a
hostile-serial test whose probe had drifted one character from its own fixture.
Copilot AI lite review requested due to automatic review settings August 29, 2026 09:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Timdebruijn
Timdebruijn merged commit b817225 into main Aug 29, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants