DEVLOG #22
I fixed the last browser security gap found in the local security review: a page on one local port can no longer reach an unauthenticated service on another local port by default.
The review contained 13 entries. Entries 8 and 11, and entries 12 and 13, were duplicates, leaving 11 unique findings.
The old check trusted the hostname alone, so every localhost port was accepted. I separated browser Origin validation from the existing Host/DNS-rebinding protection. WebSockets and state-changing /api/v1/* requests now require an exact scheme, host, and port.
By default, only http://localhost:<http_port>, http://127.0.0.1:<http_port>, and http://[::1]:<http_port> are allowed. Repeatable --allowed-origin values or the comma-separated SENSING_ALLOWED_ORIGINS variable explicitly replace these defaults. Host-only values, wildcards, null, paths, userinfo, and invalid ports are rejected.
The protection covers /ws/* and /api/v1/stream/pose on both the HTTP and dedicated WebSocket ports. CLI clients without an Origin header remain compatible.
The regression test now returns 403 for an unlisted cross-port UI, while an explicitly allowlisted cross-port UI still works. All 24/24 Origin/Host tests, 471 sensing-server tests (1 ignored), HomeCore server/API/auth/WebSocket tests (18 + 18 + 6 + 5), and 7 Python/firmware security-boundary tests passed. Binary checks, targeted rustfmt, and git diff --check also passed. No flash or OTA action was performed.
The ESP-IDF v5.4 build matrix is still inconclusive: the native ARM64 image is missing a containerd layer, while the AMD64 fallback crashes in the emulated Xtensa compiler inside MbedTLS. I therefore do not count the three firmware builds as passed.
Next: Solder PCB-02 first, then mount the mmWave sensor in the corner.