Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / scripts / sai-builder.service
Author[]Andy Green <andy@warmcat.com> 2026-09-06 06:35 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-09-06 06:35 UTC
Tree6645d696c75b37225440d8d09d6dfbce795c4bb1   Raw Patch
 
web: drop browser-originated watcher_services, harden server web link rx, fixes F-002
web: drop browser-originated watcher_services, harden server web link rx, fixes F-002

An unauthenticated browser could send
{"schema":"com.warmcat.sai.watcher_services"} on /sai/browse*: the
schema is in the browser rx map and its switch case fell into the
generic forward to sai-server over the nailed-up websrv ss link.  But
sai-server's web link schema map has no entry for it, so the decode
failed and websrvss_ws_rx() returned LWSSSSRET_DISCONNECT_ME, tearing
down the ss link; the retry policy reconnects after 1/2/3s backoff so
a repeat sender keeps the control plane flapping, and admin operations
forwarded on it fail while it is down.

Watcher services are a server config-file concern
(sais_config_watchers()); the schema only ever flows web -> browsers.
Give it its own case that logs and drops it, matching the loadreport
treatment from F-001.

As a second layer, sai-server no longer treats an undecodable message
on the web link as fatal: the link carries forwarded browser requests
whose content we do not control, so a message that fails to decode is
logged, its partial lwsac freed, and skipped, reserving disconnect for
protocol-level unrecoverable states.  This also fixes a small lwsac
leak on the decode-failure path.
diff --git a/src/server/s-ws-web.c b/src/server/s-ws-web.c index 0a22b2e..7b3600f 100644 --- a/src/server/s-ws-web.c +++ b/src/server/s-ws-web.c @@ -467,10 +467,19 @@ websrvss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags) lws_struct_json_init_parse(&m->ctx, NULL, &a); n = lejp_parse(&m->ctx, (uint8_t *)buf, (int)len); if (n < 0 || !a.dest) { + /* + * This link carries forwarded browser requests whose content + * we do not control, so an undecodable message is skipped, + * not fatal: tearing down the ss link here would take the + * nailed-up control channel away from every connected + * sai-web instance for the retry period. + */ lwsl_hexdump_notice(buf, len); lwsl_notice("%s: notification JSON decode failed '%s'\n", __func__, lejp_error_to_string(n)); - return LWSSSSRET_DISCONNECT_ME; + lwsac_free(&a.ac); + + return 0; } // lwsl_notice("%s: schema idx %d\n", __func__, a.top_schema_index); diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index 3ac9dc2..7c57ae5 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -1090,7 +1090,6 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf, */ break; - case SAIM_WS_BROWSER_RX_WATCHER_SERVICES: case SAIM_WS_BROWSER_RX_OPENSHELL: case SAIM_WS_BROWSER_RX_CLOSESHELL: case SAIM_WS_BROWSER_RX_PTYDATA: @@ -1106,6 +1105,17 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf, lwsl_notice("%s: dropping loadreport from browser\n", __func__); goto ok; + /* + * Watcher services are a server config-file concern; the schema only + * ever flows from us towards browsers. A browser sending one is + * meaningless: drop it locally rather than forward it, sai-server + * does not accept this schema on the web link. + */ + case SAIM_WS_BROWSER_RX_WATCHER_SERVICES: + lwsl_notice("%s: dropping watcher_services from browser\n", + __func__); + goto ok; + default: /* * No schema in the map today reaches here. If one is added
Page fetched 0s ago, creation time: 2ms (vhost etag hits: 0%, cache hits: 0%)