fix: close the token bypass below a route, review fixes for 4b

The routes matched any path below them, and a web asset name there
passed the token check: GET /backup/a.js returned the backup under
Secured. Every route is now an exact match, and a web asset sits in
the root only.

- e2e: those paths answer 401 or 404; the restore test now fails when
  the chunks skip the token check (checked with the check removed).
- WEB_CORS lets the socket take Origin: null instead of skipping the
  Origin check.
- ADR 0006, GLOSSARY and README: the Origin check in either mode, /ws
  as a public route, the Host header behind a reverse proxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-28 19:39:51 +01:00
parent ecb0260f61
commit 0ee52d6d9f
10 changed files with 105 additions and 51 deletions

View file

@ -7,10 +7,11 @@
// (docs/adr/0006-two-security-modes-eleven-http-routes.md). Paths are the
// request's path, without the query.
// The files the web UI loads. Static serving answers these alone, so the
// rolling codes in shades.cfg leave the device only through /backup.
// The files the web UI loads, which all sit in the root. Static serving
// answers these alone, so the rolling codes in shades.cfg leave the device
// only through /backup. A path below a route, such as /backup/a.js, isn't one.
inline bool isWebAsset(const char *path) {
if(!path || path[0] != '/' || strstr(path, "..")) return false;
if(!path || path[0] != '/' || strchr(path + 1, '/') || strstr(path, "..")) return false;
if(strcmp(path, "/") == 0 || strcmp(path, "/appversion") == 0) return true;
static const char *const types[] = {".html", ".js", ".css", ".png", ".svg", ".ico"};
const size_t len = strlen(path);