Skip to content

feat(echo): the websocket handshake guard - #26

Merged
rennf93 merged 2 commits into
masterfrom
feat/websocket-guard
Oct 7, 2026
Merged

rennf93 merged 2 commits into
masterfrom
feat/websocket-guard

Conversation

@rennf93

@rennf93 rennf93 commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

What

Ports the reference websocket guard (fastapi-guard guard/websocket.py, guard_websocket / make_guard_websocket) onto the echo adapter - the family websocket parity item (FEATURE_MATRIX_GO section 2, the echo-column MISSING row).

How

  • GuardWebSocket(engine, c) *guardcore.WebSocketCloseReason: the websocket handler calls it before upgrading. The engine runs the reference _run_websocket_checks sequence (identity, fail-secure unknown-address close, ban, is_ip_allowed, the "ws" rate limit, shared-counts penetration detection) and the adapter rejects the handshake on a close reason. Nil allows; a nil engine fails closed.
  • WebSocketHTTPStatus(reason): a rejected upgrade has no websocket to close, so the reason maps onto the HTTP rejection status (503 try-again-later, 403 policy violation). A handler that already accepted closes with the reason's code/reason verbatim.
  • The ws request shim mirrors the reference _WebSocketGuardRequest: method "WEBSOCKET", empty body, repeated headers joined with ", " (_join_repeated_header_lines), fresh request state.
  • go.mod floors guard-core-go at the websocket-guard engine pseudo-version, no release created.

Gates

  • gofmt, go vet, staticcheck clean; unit + integration suites green against local redis; the 100% coverage gate green (100.0%)

Ports the reference websocket guard (fastapi-guard guard/websocket.py
guard_websocket / make_guard_websocket) onto the echo adapter:

- GuardWebSocket(engine, c) runs the engine's websocket handshake checks
  over the upgrade request (identity, fail-secure unknown-address close,
  ban, is_ip_allowed, the ws rate limit, and the penetration detection
  pass sharing the HTTP pipeline's suspicious counts). A nil result
  allows the upgrade; a non-nil reason closes it. A nil engine fails
  closed with the security-check-failed reason.
- WebSocketHTTPStatus maps a close reason onto the HTTP status an
  upgrade rejection carries (503 try-again-later, 403 policy violation).
- The ws request shim mirrors the reference _WebSocketGuardRequest:
  method WEBSOCKET, empty body, repeated headers joined with a comma
  (_join_repeated_header_lines), fresh request state.

Floors guard-core-go at the websocket-guard pseudo-version.
@rennf93 rennf93 added the no-issue Chore or dependency PR that does not need an issue label Oct 7, 2026
@github-actions github-actions Bot added dependencies go.mod or go.sum changes tests Test suite changes build Module files / packaging labels Oct 7, 2026
@rennf93
rennf93 merged commit a19f7fa into master Oct 7, 2026
14 of 15 checks passed
@rennf93
rennf93 deleted the feat/websocket-guard branch October 7, 2026 22:00
rennf93 added a commit that referenced this pull request Oct 8, 2026
Ports the fastapi-guard middleware lifecycle ops the FEATURE_MATRIX_GO
adapter row left PARTIAL/MISSING (guard/middleware.py): mark_initialized
(:131), get_initialization_status (:705), reset (:685), agent_stats
(:256) and refresh_cloud_ip_ranges (:661), as explicit functions over
the engine handle (the same seam as GuardWebSocket and AddStatusRoute;
the reference ops are methods on SecurityMiddleware because the Python
middleware owns its machinery, while this adapter's machinery is the
engine New receives). The engine side (Initialize/Close,
MarkInitialized, AgentStats with the AgentStatsProvider seam) landed in
guard-core-go #66; the agent-side get_stats dict view in guard-agent-go
#26.

- MarkInitialized: a warmed engine makes Initialize a no-op.
- GetInitializationStatus: the payload AddStatusRoute serves.
- Reset: the rate limiter's windows (redis and in-memory).
- AgentStats: enabled/degraded merged with the wired handler's stats.
- RefreshCloudIPRanges: the redis-backed refresh at the configured TTL
  (the reference cloud_handler.refresh_async) or the in-memory refresh
  (the reference cloud_handler.refresh); no blocked providers is the
  reference's no-op early return.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
rennf93 added a commit that referenced this pull request Oct 9, 2026
… lifecycle surface - floor guard-core-go v4.3.2 (#30)

The family 1.4.0 wave on Echo: GuardWebSocket plus WebSocketHTTPStatus
(#26), AddStatusRoute (#27), the adapter lifecycle surface (MarkInitialized,
GetInitializationStatus, Reset, AgentStats, RefreshCloudIPRanges, #28).
Raises the engine floor to v4.3.2, pins golang.org/x/net v0.60.0 for the
five 2026 http2 GO advisories reachable through the cloud-refresh
lifecycle, and carries the post-transfer Guard-Core metadata sweep.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Module files / packaging dependencies go.mod or go.sum changes no-issue Chore or dependency PR that does not need an issue tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant