fix(ocap-kernel): restrict plain ws:// relay dialing to private/allowed addresses#857
Open
fix(ocap-kernel): restrict plain ws:// relay dialing to private/allowed addresses#857
Conversation
…ed addresses Adds a connectionGater.denyDialMultiaddr implementation that blocks plain ws:// connections to public internet addresses. Private/loopback IPs (RFC 1918, 127.0.0.0/8, IPv6 ULA/link-local) and an explicit per-instance allowedWsHosts list are always permitted; everything else over plain ws:// is denied. wss://, WebRTC, and circuit-relay addresses are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
… positives
The startsWith('fc'/'fd'/'fe80:') checks in isPrivateAddress could
match DNS hostnames like 'fcevil.attacker.com', allowing plain ws://
connections to attacker-controlled hosts. Fix by validating the host is
an actual IPv6 address using the URL parser (new URL('http://[<host>]')
throws for non-IPv6 strings) before applying the prefix checks. Adds a
regression test for the bypass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR #855 switched
webSockets()to usewsFilters.allto enable plainws://connections for private-network and reverse-proxy relay scenarios. As written, this also allowedws://to any public internet address, sending unencrypted traffic over the open internet.This PR keeps the transport-level change (
wsFilters.allmust stay so the transport handlesws://at all) and adds enforcement inconnectionGater.denyDialMultiaddr.Changes
types.ts: AddsallowedWsHosts?: string[]to bothRemoteCommsOptionsandConnectionFactoryOptions— an explicit per-instance list of hostnames/IPs that are trusted for plainws://beyond the always-allowed private ranges.transport.ts: ThreadsallowedWsHoststhrough toConnectionFactory.make().connection-factory.ts: Replaces theasync () => falsestub with real gating logic via two new module-level helpers:isPlainWs(ma)— detects plainws://usingma.protoNames()(true only whenwsis present and neitherwssnortlsis).isPrivateAddress(host)— checks IPv4 loopback (127.0.0.0/8), RFC 1918 ranges, IPv6 loopback/ULA/link-local by direct octet comparison (no bitwise operators).denyDialMultiaddrallowswss://, WebRTC, and circuit-relay addresses unconditionally; allowsws://only to private/loopback IPs or hosts onallowedWsHosts; denies everything else.connection-factory.test.ts: Replaces the single stub test with a 9-caseit.eachsuite covering secure transports, all four private IPv4 ranges, public IP denial, allowlisted/non-allowlisted hostnames, and non-WebSocket multiaddrs.Testing
The new
connectionGater.denyDialMultiaddrdescribe block exercises the gating function directly by passing minimalMultiaddr-shaped objects (withprotoNames()andtoOptions()) extracted from thecreateLibp2pmock call args. This avoids any libp2p networking and keeps the tests fast and deterministic.Note
Medium Risk
Changes libp2p dialing policy for
ws://by denying public destinations unless explicitly allowlisted, which could break existing relay connectivity if deployments relied on public plain WebSocket relays. Logic is security-sensitive (transport encryption), but scope is contained to connection gating and option plumbing.Overview
Prevents unencrypted
ws://dials to arbitrary public addresses by implementingconnectionGater.denyDialMultiaddrinConnectionFactory: non-plain-WebSocket multiaddrs (e.g.wss, WebRTC, circuit relay) are still allowed, while plainwsis only allowed for private/loopback IPs orallowedWsHosts.Threads a new
allowedWsHosts?: string[]option fromRemoteCommsOptions→initTransport→ConnectionFactoryOptions, and replaces the prior stubbed gater test with a parameterized suite covering private ranges, allowlisting, and public-deny cases.Written by Cursor Bugbot for commit e896ead. This will update automatically on new commits. Configure here.