fix(desktop): harden link-title previews against SSRF - #65613
HengYangDS wants to merge 10 commits into
Conversation
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Comment
Scope
- 18 files, +2658/-151 lines. Hardens link-title previews against SSRF.
Quality
- Substantial security hardening for link-title previews.
- Multi-platform impact (desktop + web).
- High file count warrants careful security review.
Notes
- SSRF hardening is security-sensitive — recommend human reviewer verify the URL-fetch isolation is complete.
Looks Good
- Security-focused fix with significant scope.
Reviewed by Hermes Agent
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the substantial DNS-pinning and transport hardening. The SSRF premise is confirmed on current main: apps/desktop/electron/main.ts:3955-3977 follows redirects with curl, and :4102-4107 loads the fallback URL in a hidden renderer.
Problems
- The PR still loads arbitrary admitted public pages in
apps/desktop/electron/main.ts:4298.apps/desktop/electron/link-title-window.ts:34-35blocks popups/WebRTC, but does not address credential UI. The related #63171 discussion documents a Windows 11 reproduction where this renderer path caused a native passkey dialog fordocs.google.com. Please remove the renderer fallback or add a tested session-level suppression for that UI before navigation. apps/desktop/electron/link-title-window.e2e.ts:10is not run by the configured test projects:apps/desktop/vitest.config.ts:19only includeselectron/**/*.test.ts, and no runner references the E2E file or its required receipt environment variable. Wire it into a deterministic Electron test command.
This is an automated hermes-sweeper review.
| @@ -26,6 +30,19 @@ export function linkTitleWindowOptions(partitionSession) { | |||
| export function createLinkTitleWindow(BrowserWindow, partitionSession) { | |||
| const window = new BrowserWindow(linkTitleWindowOptions(partitionSession)) | |||
|
|
|||
| try { | |||
| window.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) | |||
| window.webContents.setWebRTCIPHandlingPolicy('disable_non_proxied_udp') | |||
There was a problem hiding this comment.
This still leaves arbitrary admitted public pages running in the hidden renderer. The related #63171 discussion reports a Windows passkey dialog triggered by this exact fallback path; WebRTC/popup controls do not cover that. Please remove the fallback or add and test a title-session policy that suppresses credential/WebAuthn UI before navigation.
| import { startLinkTitleSocksGateway } from './link-title-socks' | ||
| import { configureLinkTitleSession, createLinkTitleWindow, readLinkTitleWindowTitle } from './link-title-window' | ||
|
|
||
| const receiptPath = process.env.HERMES_LINK_TITLE_E2E_RECEIPT |
There was a problem hiding this comment.
This program is not selected by apps/desktop/vitest.config.ts (electron/**/*.test.ts only), and no repository runner references this file or sets this required variable. Please wire it into a deterministic Electron test command so the proxy fail-closed assertion runs in CI.
8a65ce1 to
8a8c5dc
Compare
|
Addressed in Removed the hidden Validated with Node 22: focused Electron 45 files / 451 passed / 1 skipped; full Electron 48 files / 476 passed / 1 skipped; UI 193 files / 1574 passed / 1 skipped; typecheck; lint (existing warnings only); Prettier; build. |
|
Maintainer-ready at CI for this fork PR is currently |
6a3834c to
f63dc4e
Compare
f63dc4e to
fa105d1
Compare
18344ef to
aebcb4b
Compare
|
Rebased to current upstream New head: Fresh Node 22 evidence on this exact base:
Review boundary: automatic link-title preview stays on the admitted DNS-pinned curl/SOCKS path. The hidden The fork workflow remains |
7250557 to
0a94bd4
Compare
(cherry picked from commit 8f453576afaca7660857643e29952c2e88e507d8)
(cherry picked from commit 911117d3f116e11ba485e2d5154967e4858643f0)
(cherry picked from commit 97e65daf946d7473fb7e1417112db01d12f6c2bb)
(cherry picked from commit bf17aff2b28ec3ea47ddb615d90e8d207dff262f)
(cherry picked from commit 5a6995a605102336709ce1dadecbb094896e632c)
(cherry picked from commit 42cfe35cea18db1c1d9fc5db5d77b1497967b75a)
(cherry picked from commit d5195829636c215a19c5985bd2b33cecabe8f0e6)
(cherry picked from commit 97ec0961da783797a27fbcfe9a97f9fd33baea61)
(cherry picked from commit 20f88a3422f52315417a95be2242a15a469a7272)
0a94bd4 to
e8266b1
Compare
|
Rebased and force-with-lease updated to current
Fresh evidence on that exact head:
Security boundary unchanged: link-title preview uses the admitted DNS-pinned curl/SOCKS path only; the hidden renderer fallback and its test file are absent, so a curl miss returns no title rather than navigating a renderer. The fork-only run is supplemental evidence; the upstream |
|
Closing at author request: I am standardizing my local Hermes use on the official Homebrew Desktop/CLI path and will no longer maintain this fork-based contribution lane. The reviewed security work remains preserved in the PR history; maintainers may reuse it if useful. Thank you. |
Current change
Current head:
8a8c5dcb— rebased onNousResearch/maine598cef.Desktop link-title previews now admit public HTTP(S) targets and redirect hops before a fetch, pin admitted DNS results into the SOCKS/curl transport, and fail closed when that transport cannot be preserved. The follow-up removes the hidden
BrowserWindowrenderer fallback entirely: an admitted URL is never renderer-navigated.Root cause and security boundary
Automatic title resolution had multiple network paths. URL-shape checks alone did not consistently protect against redirect hops, DNS rebinding, proxy-environment bypass, or special-purpose/private addresses. The renderer fallback also created a separate native-browser surface that could trigger credential/passkey UI.
Review disposition
The renderer/passkey and unregistered-E2E concerns were addressed by deleting the renderer queue/window/E2E path rather than adding another suppression layer. Resolution is now curl-only on the pinned SOCKS path; a curl miss returns no title rather than navigating a renderer.
Validation
Node 22:
Review focus
Please review the boundary that no link-title path can renderer-navigate an admitted URL, and that redirect/DNS admission remains pinned to the curl/SOCKS transport.