Slowloris probe targets the Node editor proxy, not the Rust server #228

Closed
opened 2026-09-27 10:49:28 +00:00 by kayg · 7 comments
Owner

Finding

The exhaustion probe opens its slowloris sockets on HOST, PORT in tests/adversarial/attack2.py. PORT is ADVERSARIAL_PORT, which tests/adversarial/run.sh assigns to the Node HTTP/WebSocket proxy in tests/adversarial/editor-proxy.mjs. The proxy forwards a request only after Node parses its HTTP headers. A silent socket or a partial header therefore stays in Node; it never reaches the Rust server's serve.rs connection loop or its 20-second HEADER_READ_TIMEOUT.

The probe reports both sockets still open after 35 seconds. This is not evidence that the Rust server ignored its timeout. Use ADVERSARIAL_BACKEND_PORT for the raw silent/partial-header sockets, or give the Node proxy its own bounded header timeout and report it separately. Then verify the Rust server closes those sockets within the configured limit.

Evidence: target/tmp/menu-icons-adversarial-latest.log in the menu-icons worktree. The same probe path and result also appeared in the concurrent heading-links run. Server stayed alive. This is a harness target error; no backend DoS has been established.

## Finding The exhaustion probe opens its slowloris sockets on `HOST, PORT` in `tests/adversarial/attack2.py`. `PORT` is `ADVERSARIAL_PORT`, which `tests/adversarial/run.sh` assigns to the Node HTTP/WebSocket proxy in `tests/adversarial/editor-proxy.mjs`. The proxy forwards a request only after Node parses its HTTP headers. A silent socket or a partial header therefore stays in Node; it never reaches the Rust server's `serve.rs` connection loop or its 20-second `HEADER_READ_TIMEOUT`. The probe reports both sockets still open after 35 seconds. This is not evidence that the Rust server ignored its timeout. Use `ADVERSARIAL_BACKEND_PORT` for the raw silent/partial-header sockets, or give the Node proxy its own bounded header timeout and report it separately. Then verify the Rust server closes those sockets within the configured limit. Evidence: `target/tmp/menu-icons-adversarial-latest.log` in the menu-icons worktree. The same probe path and result also appeared in the concurrent `heading-links` run. Server stayed alive. This is a harness target error; no backend DoS has been established.
Author
Owner

Additional evidence from the same run: analytics("period=week&tz=" + "x" * 70000) uses req() in attack2.py, which connects to ADVERSARIAL_PORT (the Node proxy). It returned NO RESPONSE (Connection reset by peer), so the oversized request target may be rejected before the Rust analytics route. Please send this oversized-request-line probe to ADVERSARIAL_BACKEND_PORT too, or report proxy and backend outcomes separately.

Additional evidence from the same run: `analytics("period=week&tz=" + "x" * 70000)` uses `req()` in `attack2.py`, which connects to `ADVERSARIAL_PORT` (the Node proxy). It returned `NO RESPONSE (Connection reset by peer)`, so the oversized request target may be rejected before the Rust analytics route. Please send this oversized-request-line probe to `ADVERSARIAL_BACKEND_PORT` too, or report proxy and backend outcomes separately.
Author
Owner

The latest post-merge adversarial round again reported the silent and partial-header sockets open after 35 seconds. They connect to ADVERSARIAL_PORT, the Node proxy, not the Rust backend. The focused Rust test serve::tests::silent_partial_and_idle_connections_are_closed passed (1 passed, 0 failed) with a 300 ms timeout.

The same proxy path returned its own 502 local adversarial server is unavailable for the 70 KB Analytics query and oversized AI undo body after upstream connection resets. Those responses do not establish that the Rust handlers returned 502. Consider sending hostile HTTP checks directly to the backend or preserving the backend's early error response through the proxy.

The latest post-merge adversarial round again reported the silent and partial-header sockets open after 35 seconds. They connect to `ADVERSARIAL_PORT`, the Node proxy, not the Rust backend. The focused Rust test `serve::tests::silent_partial_and_idle_connections_are_closed` passed (1 passed, 0 failed) with a 300 ms timeout. The same proxy path returned its own `502 local adversarial server is unavailable` for the 70 KB Analytics query and oversized AI undo body after upstream connection resets. Those responses do not establish that the Rust handlers returned 502. Consider sending hostile HTTP checks directly to the backend or preserving the backend's early error response through the proxy.
Author
Owner

This worktree's adversarial run reproduced both probe messages: slowloris silent: connection still open after 35 s and slowloris partial header: connection still open after 35 s. The run uses the current harness and the result is already tracked here; it does not establish a Rust server timeout failure because the probe connects through the Node proxy. Full log: target/tmp/phone-chrome-adversarial-retry.log in the phone-chrome worktree.

This worktree's adversarial run reproduced both probe messages: `slowloris silent: connection still open after 35 s` and `slowloris partial header: connection still open after 35 s`. The run uses the current harness and the result is already tracked here; it does not establish a Rust server timeout failure because the probe connects through the Node proxy. Full log: `target/tmp/phone-chrome-adversarial-retry.log` in the phone-chrome worktree.
Author
Owner

The post-merge adversarial round for CSP issue #118 reproduced both !! slowloris silent: connection still open after 35 s and !! slowloris partial header: connection still open after 35 s. This is the same known target error in #228: these sockets go to the Node proxy, not the Rust server's header-timeout path. The server remained alive; this result does not establish a backend timeout failure.

The post-merge adversarial round for CSP issue #118 reproduced both `!! slowloris silent: connection still open after 35 s` and `!! slowloris partial header: connection still open after 35 s`. This is the same known target error in #228: these sockets go to the Node proxy, not the Rust server's header-timeout path. The server remained alive; this result does not establish a backend timeout failure.
Author
Owner

Post-merge adversarial run at c2ff7b40: the silent-header and partial-header Slowloris sockets remained open after the probe's 35-second deadline. This probe targets the Node editor proxy path; the run overlapped other builds and probes. Please replay against the intended service in isolation.

Post-merge adversarial run at c2ff7b40: the silent-header and partial-header Slowloris sockets remained open after the probe's 35-second deadline. This probe targets the Node editor proxy path; the run overlapped other builds and probes. Please replay against the intended service in isolation.
Author
Owner

Post-merge replay exercised silent and partial-header sockets directly against ADVERSARIAL_BACKEND_PORT (the Rust server), per this issue's finding. The exhaustion probe reported no slowloris finding, and the server remained alive at the end.

Post-merge replay exercised silent and partial-header sockets directly against `ADVERSARIAL_BACKEND_PORT` (the Rust server), per this issue's finding. The exhaustion probe reported no slowloris finding, and the server remained alive at the end.
Author
Owner

Fixed in 1fe908747c on origin/dev: tests/adversarial/attack2.py now sends silent and partial-header Slowloris sockets to HTTP_SERVER_PORT, which is ADVERSARIAL_BACKEND_PORT. The direct-backend replay recorded on this issue reported no Slowloris finding; the exhaustion probe still checks the 35-second close bound.

Fixed in 1fe908747c on origin/dev: tests/adversarial/attack2.py now sends silent and partial-header Slowloris sockets to HTTP_SERVER_PORT, which is ADVERSARIAL_BACKEND_PORT. The direct-backend replay recorded on this issue reported no Slowloris finding; the exhaustion probe still checks the 35-second close bound.
kayg closed this issue 2026-10-03 12:41:35 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#228
No description provided.