• websrvr: uncaught script errors are sent to the HTTP client (path disc

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Tue Jul 28 09:02:24 2026
    open https://gitlab.synchro.net/main/sbbs/-/issues/1203

    `js_ErrorReporter()` in `src/sbbs3/websrvr.cpp` writes every uncaught JavaScript
    error — the message text, plus the script's **absolute** filename and line number — into the HTTP response body, in addition to logging it:

    ```cpp
    lprintf(log_level, "%04d %-5s [%s] !JavaScript %s%s%s: %s, Request: %s"
    , session->socket, session->client.protocol, session->host_ip, warning, file, line, message, session->req.request_line);
    if (content_file_open(session))
    fprintf(session->req.fp, "!JavaScript %s%s%s: %s", warning, file, line, message);
    ```

    (same in the `report == NULL` branch above it). This is a useful debugging aid, but sending it to the *client* has three problems:

    **1. Information disclosure.** Any remote visitor who can make a script throw learns the server's on-disk layout and the failing source file and line — e.g.
    `s:\sbbs\webv4\lib/pages.js line 108`.

    **2. Reflected XSS whenever the message contains request data.** The error text is emitted as the page body with the page's own content type (`text/html` for a `.xjs`/`.ssjs` page) and an HTTP **200**, so any `throw` that interpolates a query parameter, header, or cookie becomes a script-injection vector. This was live in stock webv4 until today. Against an unpatched tree:

    ```
    GET /index.xjs?page=<b>bold</b><script>alert(1)</script>.link

    HTTP/1.1 200 OK
    Content-Type: text/html

    !JavaScript s:\sbbs\webv4\lib/pages.js line 108: Error: Invalid page <b>bold</b><script>alert(1)</script>.link,,,s:\sbbs\webv4\mods\pages\
    ```

    The payload runs in the visitor's browser in the BBS's origin, where webv4's login session cookie lives. That particular `throw` is gone as of 791eb3ad3c, but the delivery mechanism is still there for any other stock or sysop-authored page that puts request data in an error message.

    **3. Wrong status and corrupted output.** A failed script yields `200 OK` with error text as the body, rather than a 500 — and if the script had already buffered content, the error is appended to a partial page (or to a JSON API response, breaking the parse client-side).

    ### Suggested fix

    Log the error, don't send it. Options, roughly in order of preference:

    - Drop the `fprintf()` to `session->req.fp` and let the request fail through the
    normal error-document path (`ErrorDirectory`), returning 500.
    - Or keep the echo behind a new `[Web]` option defaulting to **off** (something
    like `JavaScriptErrorsToClient=false`), for sysops who want it while
    developing.

    HTML-escaping the message is not a sufficient fix on its own: the response may be JSON or plain text, and the path disclosure remains either way.

    — *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)