Skip to content

fix(gateway): bound websocket auth after valid connect#73925

Closed
openclaw-clownfish[bot] wants to merge 1 commit into
mainfrom
clownfish/ghcrawl-156675-autonomous-smoke
Closed

fix(gateway): bound websocket auth after valid connect#73925
openclaw-clownfish[bot] wants to merge 1 commit into
mainfrom
clownfish/ghcrawl-156675-autonomous-smoke

Conversation

@openclaw-clownfish

Copy link
Copy Markdown
Contributor

Repair contributor PR #49751 in place.

Plan:

  • Rebase the existing branch onto current main 381c2e1.
  • Keep clearing the preauth timer only after a valid connect frame is received.
  • Keep a bounded connect-auth watchdog so stalled auth cannot leave sockets open indefinitely.
  • Confirm the old client.ts connectDelayMs clamp review finding is obsolete against the current branch, or remove that behavior if it reappears.
  • Run Codex /review, address every finding, then run pnpm check:changed plus the focused gateway Vitest command.

Credit: based on @sashakhar1's original PR #49751, with ProjectClownfish repair commits preserving the contributor path.

ProjectClownfish replacement details:

@openclaw-clownfish openclaw-clownfish Bot added the clawsweeper Tracked by ClawSweeper automation label Apr 29, 2026
@aisle-research-bot

aisle-research-bot Bot commented Apr 29, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟠 High Connect-auth watchdog can be reset by repeated connect frames (pre-auth DoS)
1. 🟠 Connect-auth watchdog can be reset by repeated `connect` frames (pre-auth DoS)
Property Value
Severity High
CWE CWE-400
Location src/gateway/server/ws-connection/message-handler.ts:423-427

Description

The websocket handshake path arms a new connect-auth timeout watchdog when the first connect request is parsed. However, the handler does not mark the connection as "connect in progress" before awaiting async auth, and it continues to treat the socket as "not yet connected" as long as getClient() is null.

As a result, a client can send additional connect request frames while the first one is still awaiting resolveConnectAuthState(...) (or other async steps). Each additional connect frame executes:

  • clearHandshakeTimer()
  • armConnectAuthTimer() (which clears and re-sets the timeout)

This allows an unauthenticated peer to indefinitely postpone the auth-timeout by repeatedly sending connect frames, keeping the socket and the pre-auth connection budget slot held, which can lead to resource exhaustion / connection starvation.

Vulnerable code:

const connectParams = frame.params as ConnectParams;
clearHandshakeTimer();
armConnectAuthTimer();// ... await resolveConnectAuthState(...)

Because the handler awaits auth before calling setClient(...), getClient() remains null and subsequent messages re-enter the same handshake path.

Recommendation

Prevent handshake state-machine re-entry while auth is in progress.

Options:

  1. One-shot arm: only allow armConnectAuthTimer() to run once per connection, and ignore/close on subsequent connect frames until the first finishes.

  2. Explicit state: introduce a handshakeState/flag like connectInProgress (or set handshakeState to "connecting") before the first await, and reject subsequent connect frames when this flag is set.

Example (sketch):

let connectInProgress = false;

if (!client) {
  if (connectInProgress) {
    send({ type: "res", id: parsed.id, ok: false, error: errorShape(ErrorCodes.INVALID_REQUEST, "connect already in progress") });
    close(1008, "duplicate connect");
    return;
  }
  connectInProgress = true;
  clearHandshakeTimer();
  armConnectAuthTimer();
  try {
    const state = await resolveConnectAuthState(...);// ... continue handshake
  } finally {
    connectInProgress = false;
  }
}

Also consider not clearing/rearming the watchdog if it is already armed, so repeated frames cannot extend the deadline.


Analyzed PR: #73925 at commit 9a16c5b

Last updated on: 2026-04-29T01:38:16Z

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime size: M labels Apr 29, 2026
@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR repairs contributor PR #49751 to split the single preauth handshake timer into a two-phase design: the original timer fires only if no valid connect frame arrives, while a new connectAuthTimer watchdog starts when a valid frame is received and bounds the async auth phase. Two isClosed() guards are also added after each await in the connect handler to bail out if the watchdog fires during auth.

  • P1 — auth watchdog can be reset: armConnectAuthTimer calls clearConnectAuthTimer() before arming the new timer (see ws-connection.ts:408). Because handleMessage is async and has no concurrency guard, a second valid connect frame arriving while the first auth awaits resolveConnectAuthState will call armConnectAuthTimer again and silently reset the timeout window. A client can repeat this every handshakeTimeoutMs - ε ms to hold the connection open indefinitely, negating the bounded-auth guarantee the PR is trying to provide.

Confidence Score: 3/5

The auth watchdog's core invariant — that stalled auth is bounded — can be bypassed by re-sending valid connect frames before merging.

A single P1 finding directly undermines the security property this PR introduces. The rest of the implementation (timer lifecycle, isClosed guards, test coverage) is correct, so this is not a full stop, but the watchdog bypass should be addressed before merging.

src/gateway/server/ws-connection.ts — armConnectAuthTimer needs a one-shot guard; src/gateway/server/ws-connection/message-handler.ts — call site at lines 425–426.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/server/ws-connection/message-handler.ts
Line: 425-426

Comment:
**Auth watchdog can be reset by repeated valid connect frames**

`armConnectAuthTimer` calls `clearConnectAuthTimer()` internally before arming a new timer (see `ws-connection.ts:408`). The `handleMessage` handler is `async` and has no concurrency guard, so while the first auth is suspended at `await resolveConnectAuthState(...)`, a second valid `connect` frame that arrives will re-enter this block (`!client` is still true), call `armConnectAuthTimer()` again, and silently reset the watchdog. A client can repeat this every `handshakeTimeoutMs - ε` ms to hold the socket open indefinitely, which defeats the PR's core goal of bounding connect-auth duration.

A simple guard is to arm the timer only once — e.g. check `connectAuthTimer` before overwriting it:

```ts
const armConnectAuthTimer = () => {
  if (connectAuthTimer) {
    return; // already armed; do not reset
  }
  clearHandshakeTimer();
  // ...rest of the existing body
};
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(gateway): bound websocket auth after..." | Re-trigger Greptile

Comment on lines +425 to +426
clearHandshakeTimer();
armConnectAuthTimer();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Auth watchdog can be reset by repeated valid connect frames

armConnectAuthTimer calls clearConnectAuthTimer() internally before arming a new timer (see ws-connection.ts:408). The handleMessage handler is async and has no concurrency guard, so while the first auth is suspended at await resolveConnectAuthState(...), a second valid connect frame that arrives will re-enter this block (!client is still true), call armConnectAuthTimer() again, and silently reset the watchdog. A client can repeat this every handshakeTimeoutMs - ε ms to hold the socket open indefinitely, which defeats the PR's core goal of bounding connect-auth duration.

A simple guard is to arm the timer only once — e.g. check connectAuthTimer before overwriting it:

const armConnectAuthTimer = () => {
  if (connectAuthTimer) {
    return; // already armed; do not reset
  }
  clearHandshakeTimer();
  // ...rest of the existing body
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server/ws-connection/message-handler.ts
Line: 425-426

Comment:
**Auth watchdog can be reset by repeated valid connect frames**

`armConnectAuthTimer` calls `clearConnectAuthTimer()` internally before arming a new timer (see `ws-connection.ts:408`). The `handleMessage` handler is `async` and has no concurrency guard, so while the first auth is suspended at `await resolveConnectAuthState(...)`, a second valid `connect` frame that arrives will re-enter this block (`!client` is still true), call `armConnectAuthTimer()` again, and silently reset the watchdog. A client can repeat this every `handshakeTimeoutMs - ε` ms to hold the socket open indefinitely, which defeats the PR's core goal of bounding connect-auth duration.

A simple guard is to arm the timer only once — e.g. check `connectAuthTimer` before overwriting it:

```ts
const armConnectAuthTimer = () => {
  if (connectAuthTimer) {
    return; // already armed; do not reset
  }
  clearHandshakeTimer();
  // ...rest of the existing body
};
```

How can I resolve this? If you propose a fix, please make it concise.

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep open, but not merge-ready: the branch targets a real Gateway auth-timeout gap, yet the proposed connect-auth watchdog can be reset before a client is registered, the PR is currently conflicting with main, and after-fix real behavior proof is absent.

Canonical path: Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

Do we have a high-confidence way to reproduce the issue?

Yes by source inspection: while connect auth awaits, getClient() remains null and another valid connect frame can re-enter the PR's pre-client path and rearm the watchdog. I did not run a live socket repro in this read-only review.

Is this the best way to solve the issue?

No as submitted; the connection/message-handler boundary is plausible, but the deadline must be one-shot or duplicate connect frames must be rejected before this is the best fix. The branch also needs rebase and real behavior proof.

Security review:

Security review needs attention: The diff introduces a concrete Gateway preauth DoS bypass unless duplicate in-flight connect frames cannot reset the auth watchdog.

  • [high] Resettable connect-auth watchdog — src/gateway/server/ws-connection/message-handler.ts:426
    Repeated valid pre-client connect frames can rearm the proposed watchdog before a client is registered, allowing an unauthenticated peer to extend the auth window and hold Gateway resources.
    Confidence: 0.9

AGENTS.md: found and applied where relevant.

What I checked:

  • stale F-rated PR: PR was opened 2026-04-29T01:35:55Z, is older than 60 days, and the latest review rated it F.
  • proof blocker: real behavior proof is missing and proof tier is F, so this branch is not merge-ready without contributor follow-up.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • steipete: Git history on the Gateway auth/handshake files shows Peter Steinberger carried major refactors and timeout/auth wiring work that this PR builds on. (role: recent adjacent Gateway auth contributor; confidence: medium; commits: f5408d82d210, bbdfba569440, 0c1f491a0262; files: src/gateway/server/ws-connection/message-handler.ts, src/gateway/server/ws-connection.ts, src/gateway/handshake-timeouts.ts)
  • vincentkoc: The source and replacement PR discussions credit Vincent Koc with the ProjectClownfish repair path and targeted Gateway validation around this exact timer/auth behavior. (role: branch repair coordinator and adjacent Gateway contributor; confidence: medium; commits: 9a16c5bb391a, 98422460f08d; files: src/gateway/server/ws-connection/message-handler.ts, src/gateway/server/ws-connection.ts, src/gateway/server/ws-connection/message-handler.post-connect-health.test.ts)
  • sashakhar1: The replacement PR is based on sashakhar1's closed source PR and the linked issue comments include the slow-auth Gateway reproduction that motivated this repair. (role: original reproducer and source PR author; confidence: medium; commits: 683db39fdab9, 98422460f08d; files: src/gateway/server/ws-connection/message-handler.ts, src/gateway/client.ts)
  • Dallin Romney: Current-main blame on the central WebSocket connection and message-handler lines points to a recent broad commit touching these files, so this is a routing signal rather than semantic ownership. (role: recent current-main toucher; confidence: low; commits: 2ec670898018; files: src/gateway/server/ws-connection/message-handler.ts, src/gateway/server/ws-connection.ts)
  • openclaw/openclaw-secops: CODEOWNERS explicitly covers src/gateway auth files, and this PR changes preauth/connect-auth timing around authorization. (role: CODEOWNERS reviewer team; confidence: high; files: .github/CODEOWNERS, src/gateway/server/ws-connection/message-handler.ts)

Codex review notes: model internal, reasoning high; reviewed against 738b2be4b49b.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label May 30, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels May 30, 2026
@barnacle-openclaw barnacle-openclaw Bot removed the stale Marked as stale due to inactivity label May 30, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels May 30, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jun 14, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Jun 16, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 17, 2026
@clawsweeper clawsweeper Bot added status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 17, 2026
@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 29, 2026
@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clawsweeper Tracked by ClawSweeper automation docs Improvements or additions to documentation gateway Gateway runtime merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants