ESPSomfy-RTS/docs/KNOWN_ISSUES.md
cjkas 43191ba312 feat: sign tokens with a secret key, compare them in constant time
Tokens were HMAC(serverId, credentials + client IP). serverId is public
(mDNS, MQTT, /loginContext), so anyone who saw a token could test PIN
guesses offline. The key is now 32 random bytes made on first boot in
their own NVS namespace, which no settings save clears and no backup
holds. It is made before WiFi starts, from the bootloader's entropy
source.

Tokens are compared with tokenMatches, which takes the same time
whatever the guess, on the apikey header, the socket upgrade and
/login.

createAPIToken never freed its mbedtls context, so every token it made
leaked: 563 bytes per login on the device, until it ran out of memory
after about 150 logins. It now frees it: 0 bytes over 500 logins.

Every token made before the upgrade is refused once. The web UI asks
for login again, and Home Assistant 3.0 logs in again by itself.

Closes #80

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 19:41:43 +01:00

2.9 KiB

Known Issues

Tracked as GitHub issues

  • #77 Port 80 routes don't check login
  • #78 CORS allows any origin
  • #79 Websocket has no auth
  • #82 A saved WiFi passphrase or MQTT password can't be cleared from the UI
  • #83 Installing "Main" from the update dialog always fails
  • #84 Transmitting a frame stops loop() for about 365 ms
  • #85 Integrated tilt turns its slats back with an Up after a lift-only target
  • #86 ShadeBehaviour: self-echo, group commands and lastFrame (structural follow-ups)

Command replies read the live shade model

Status: open. What's left of the concurrent-mutation problem that ADR-0002 fixed for reads and edits. Location: the command handlers in src/entryPoints/Web.cpp.

Summary

The read routes stream a ModelSnapshot that loop() fills, and edits go to loop() through the LoopQueue (ADR-0002). Command replies (/shadeCommand, /tiltCommand, /unpairShade, /setRollingCode, ...) still copy the one item they report from the live shade model on async_tcp, so a reply can show a value from the middle of a change. /room, /shade, /group and /groupOptions also check on the live model whether the item exists, because the status goes out before the snapshot is filled.

A read of /controller took 2.4 s

Status: seen once, not explained. Location: the /controller route; the warning comes from logElapsed in src/Timing.h.

Summary

On 2026-10-19 the log shows web GET /controller from 192.168.1.197 total took 2353.274ms (slow). That was about 30 s after Home Assistant's socket connected, and 80 s after boot. Every other /controller read in the logs took 100-235 ms.

.gz does not exist errors are log noise

Status: open. Cosmetic. Location: serveStatic in src/entryPoints/WebUiRoutes.cpp; MINIFY_AND_GZIP in minify.py.

serveStatic tries <file>.gz first. For files stored without one (appversion, icon.png), LittleFS logs an E-level does not exist, no permits for creation line on every request. The fix would be for minify.py to gzip the plain files too; appversion must stay plain as well, because ConfigSettings reads it from LittleFS. setTryGzipFirst(false) doesn't help: the error comes from exists() probing the missing variant, so it would only move the noise to the gzip-only files.