Repository navigation
Keep remote-initiated opens off this Mac's local network - #15148
austinywang wants to merge 5 commits into
Conversation
Adds failing regression tests for: - PrivateNetworkHostPolicy: loopback, private, link-local and legacy IPv4 spellings - RemoteLinkOpenPolicy: remote-initiated opens never hand private or loopback URLs to the default browser - PrivateAddressRouteSelector: machines sharing 127.0.0.1 route only to the browser's owner - RemoteMachineNotificationSubtitle: an explicit subtitle still names the machine The sources are stubs that keep today's behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A url-open that a remote machine starts without a click no longer hands a loopback, private, link-local or local-name URL to the default browser. SSH loopback routes stay in the cmux browser and are never opened in the system browser for a remote request; refusals return false so the remote side prints the URL instead. - Browser private-address routing picks the machine that owns the browser when several SSH machines share 127.0.0.1, instead of the first match. - A cloud notification's explicit subtitle still names its machine, with line breaks, control characters and bidi overrides removed and lengths capped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 6 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository: manaflow-ai/cmux/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (22)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
All contributors have signed the CLA ✍️ ✅ |
CI failure attributionCI passes on Written by |
|
Review: security review of this one, since blocking remote-initiated local network access is the whole point. The IP handling is genuinely good, but I do not think the PR closes the hole it says it closes. Not merging, one blocker below. Blocker: hostnames are never resolved, so the guard only covers IP literals.
The function that was supposed to cover this is right there in the same file. So the concrete bypass: a remote guest goes through The test suite does not catch it because there is no test for a DNS name that needs resolution to classify correctly. Every hostname test uses a name that is already on the suffix list. Fix direction, your call which: resolve the host and re-check the resolved addresses before a remote-initiated open, or refuse remote-initiated opens for any hostname Note that resolve-then-check is still TOCTOU against real rebinding, since the browser resolves again independently. If you want that closed properly it has to be an allowlist, not a denylist. What I checked that is fine The IPv4 range table at Literal parsing tricks do not get through. Only one production call site sets One thing to keep an eye on, not a blocker
Fixed: nothing, this needs an author decision on the resolution strategy. Left: the hostname blocker above. Happy to re-review once it is wired up. |
* Test that remote terminal links never open a file on this Mac A path or file URL from an SSH, cloud or unplaced terminal, and any open a remote machine asks for, must not resolve against this Mac's filesystem. The policy stub keeps today's behavior so these fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Never open a file on this Mac from a remote terminal link Only a click in a terminal known to run on this Mac opens a local file. SSH, cloud and unplaced terminals, and any open a remote machine asks for, resolve on the remote machine or are refused, so apps and scripts on this Mac never launch from remote content. The Dock now treats unplaced and remote-machine terminals as remote. Scrollback exports Ghostty wrote on this Mac still open locally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Dogfood build of cmux DEV pr-15148-dae87743.app The link opens this exact commit in the cmux dev menu bar app. The build starts on each push and the page waits until it is ready; a newer push replaces it. It signs in against production, so Cloud or backend changes still need a tagged build with a development backend. Dogfood tours of
|
The remote tmux lifecycle test still expected a path clicked in a projected SSH-tmux pane to open the same path on this Mac through the external file opener. Remote terminal links now never open a local file, so the open is refused and nothing reaches the file opener or the system opener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Automatic catch-up couldn't merge Label |
|
Closing as superseded by merged PR #15116, which consolidated this SSH/security/local-state hardening into main. |
Summary
Tightens what a remote machine can make this Mac do without a click.
url-openfrom a cloud or SSH machine can no longer hand a loopback, private, link-local, multicast or local-name URL (localhost,*.local,*.internal, single-label names, legacy IPv4 spellings such as0x7f.1, and mapped IPv6) to the default browser.https://opens are unchanged, including OAuth and device-login pages.127.0.0.1, a browser URL now goes only to the machine that owns the browser, instead of the first machine the catalog returns.BrowserPanel.navigateandrebindCloudRouteIfNeeded.reply_shapeand click actions were already clamped for cloud rows, so they are unchanged.The decisions live in CmuxCore and CmuxNotifications value types, which the app calls:
PrivateNetworkHostPolicyRemoteLinkOpenPolicyPrivateAddressRouteSelectorRemoteMachineNotificationSubtitleRelated: #12373, #10827 and #7397 (related, no overlap).
Follow-ups (not in this PR)
file://and path links that fall through to local files. This covers:canResolveTerminalPathsAgainstLocalFilesystemshould treatisLocal != falseas remote;terminalLinkIsRemoteTerminalgap.PrivateNetworkHostPolicy.requiresAddressLookupmarks which names need it. DNS rebinding remains after that.Testing
Red commit
99a24db852has the tests and stubs that keep the old behavior. Green commit70da666651has the fix. The same focused commands were run on both:swift test --package-path Packages/macOS/CmuxCore --filter 'PrivateNetworkHostPolicyTests|RemoteLinkOpenPolicyTests|PrivateAddressRouteSelectorTests'swift test --package-path Packages/macOS/CmuxNotifications --filter RemoteMachineNotificationSubtitleTestspython3 scripts/verify-local.py --affected origin/main --swift-changed origin/mainpassed 4/4 checks: swift-syntax, wire-app-sources, package-groups and feature-flags.Not run locally, because the machine's disk was nearly full:
cmuxTests;The app-side wiring in these files has not been compiled locally; CI is the first compile:
TerminalLinkOpenCoordinatorCloudGuestURLServiceCloudNotificationLocalDeliveryBrowserPanelLocalization audit: no new user-facing strings. The subtitle reuses
cloudNotification.subtitle.machine.Changelog
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Keeps links a remote machine opens without a click off this Mac's loopback and local network, routes private-address browser URLs to the machine that owns the browser, and stops remote terminal content from opening files on this Mac.
url-openno longer hands a loopback, private, link-local, multicast, or local-name URL (localhost,*.local,*.internal, single-label names, legacy IPv4 spellings) to the default browser.https://opens and clicked links are unchanged.127.0.0.1, a browser URL now goes only to the machine that owns the browser, in bothBrowserPanel.navigateandrebindCloudRouteIfNeeded.Written for commit 2fda0cb. Summary will update on new commits.