Repository navigation
[Bug]: Link favicons send internal company hostnames to Google, and preview tabs also send the port #16894
Copy link
Copy link
Closed
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Description
Activity
Note
Grok responding on behalf of Julius.
Thanks for the detailed write-up. I checked it against
main@ 4ce6a51.Confirmed in code
- Port leak. In
packages/shared/src/favicon.ts:92-93,faviconUrlForOrigingates onisPublicFaviconHost(url.hostname)but builds the Google URL fromencodeURIComponent(url.host), so a non-default port goes out too.faviconUrlForOrigin("https://staging-api.acme-corp.com:8443/")returnshttps://www.google.com/s2/favicons?domain=staging-api.acme-corp.com%3A8443&sz=32.packages/shared/src/favicon.test.ts:30-34currently asserts this ("keeps the public origin, port and requested size"), so that test needs to change with the fix. - Preview tabs hit this path.
PreviewFavicon(apps/web/src/components/RightPanelTabs.tsx:619-627) passes the full navigated URL tofaviconUrlForOriginand hands[capturedUrl, publicProviderUrl]toFaviconImage.FaviconImage(apps/web/src/components/preview/PreviewFaviconIcon.tsx:9-43) loads the first source that hasn't failed. So when there's no captured favicon (capturedUrlis null unless the desktop capture matches the current origin,RightPanelTabs.tsx:653-660), or the captured image fails, the Google URL withhost:portis requested. - Chat links send the hostname but not the port. Web
MarkdownLinkFavicon(apps/web/src/components/ChatMarkdown.tsx:1499-1502) gets its host fromresolveExternalWebLinkHost, which returns.hostname(apps/web/src/components/chat/externalLinkContextMenu.ts:91-92). MobileMarkdownExternalLink(apps/mobile/src/features/threads/ThreadFeed.tsx:755) getsparsed.hostname(apps/mobile/modules/t3-markdown-text/src/markdownLinks.ts:275). - What the filter lets through.
isPublicFaviconHost(packages/shared/src/hostClassification.ts:123-162) rejects loopback, RFC 1918, CGNAT/Tailscale,.ts.net,.local,.localhost,home.arpa, single-label names, special-purpose IPs, and the.alt/.example/.internal/.invalid/.onion/.testsuffixes. Every other dotted name returnstrue. I ran the function onmain, and these all came back public:grafana.internal.acme-corp.com,jira.corp.acme-corp.com,staging-api.acme-corp.com,acme-corp.atlassian.net,grafana.corp,build.lanand52.1.2.3. Private and Tailscale hosts are correctly excluded, so [Bug]: favicon requests send private link hosts to Google #5843's fix still holds for those. faviconUrlForPage(favicon.ts:8-17) only builds<page origin>/favicon.ico, a first-party request with no third party involved. It's used only bytoolActivityFaviconUrl(favicon.ts:61-83), not by chat links or preview tabs.
Root cause
faviconUrlForOriginclassifies onhostnamebut sendshost. Separately, the public/private filter can only reject names that are private by syntax. It can't tell that*.corp.acme-corp.comis internal, so any publicly-shaped name a user or agent links to goes to Google.Fix direction
- Use
encodeURIComponent(url.hostname)infaviconUrlForOriginand update thefavicon.test.ts:30-34expectation. This closes the port leak for preview tabs. - The broader question is whether chat links and preview tabs should use a third-party favicon lookup at all, or have a toggle for it, versus the site's own
/favicon.icolikefaviconUrlForPage. That's a product decision and isn't part of this bug fix.
Workaround
Block
www.google.comfor T3 Code in a per-app firewall (Little Snitch/LuLu). The favicon<img>/Imageerror handlers then fall back to a placeholder glyph: a globe on web and desktop (ChatMarkdown.tsx:1510-1520,FaviconImage), and◉on mobile (ThreadFeed.tsx:772-786).- Port leak. In
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 7, 2026
Metadata
Metadata
Assignees
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
Before submitting
Area
packages/contracts or packages/shared
Steps to reproduce
Follow-up to #5843. That fix stops private hosts (localhost, RFC 1918, Tailscale,
.local,.internal, …) from going to Google. Two leaks remain for publicly-named hosts, and they matter for confidential/company work:A. Chat links send confidential hostnames to Google
https://grafana.internal.acme-corp.com/d/abc/latencyorhttps://jira.corp.acme-corp.com/browse/SEC-112.MarkdownLinkFaviconrenders and loadshttps://www.google.com/s2/favicons?domain=grafana.internal.acme-corp.com&sz=32.B. Preview tabs also send the port
https://staging-api.acme-corp.com:8443/.PreviewFaviconloadshttps://www.google.com/s2/favicons?domain=staging-api.acme-corp.com%3A8443&sz=32.Expected behavior
The app does not send the hostnames of links in my threads, or the host:port of pages I preview, to a third party. Or at least it lets me turn that off. Agents write links to internal dashboards, tickets, staging environments and self-hosted Git hosts all the time, so this happens even if the user never pastes a link.
If Google lookups are kept, the request should never include the port.
Actual behavior
faviconUrlForOrigin(packages/shared/src/favicon.ts:86-97, main @ 4ce6a51) gates onisPublicFaviconHost(url.hostname)but builds the request fromurl.host:Hostname leak.
isPublicFaviconHost(packages/shared/src/hostClassification.ts:123) only rejects names that can't resolve publicly. Any name under a registered domain passes, including ones that are obviously internal by convention. I ran the shipped filter from the 0.0.46 desktop bundle against sample hosts:grafana.internal.acme-corp.com.internalonly matches as a suffix)jira.corp.acme-corp.com,staging-api.acme-corp.com,gitlab.acme-corp.comacme-corp.atlassian.net(reveals vendor/customer relationships)grafana.corp,build.lan(common private naming not in the suffix list)52.1.2.3localhost,10.x,192.168.x,100.64/10,*.ts.net,*.local, single-label names,.internal/.testsuffixesPort leak. The filter checks
url.hostname, but the query usesurl.host, which includes the port. Chat links aren't affected, because web (resolveExternalWebLinkHost→.hostname) and mobile (markdownLinks.ts:275→parsed.hostname) pass a hostname with no port. The preview tab is affected:RightPanelTabs.tsx:619-627passes the full navigated URL tofaviconUrlForOrigin(url, 32). A non-default port on a staging, admin or debug service tells a third party how the infrastructure is laid out, and it's never needed for a favicon lookup.Call sites on main @ 4ce6a51:
What Google gets each time: the hostname (and the port in case B), the user's IP, and a timestamp. No path or query, no thread content. For people doing confidential work, though, the list of internal hostnames they look at, linked to their IP, is sensitive in itself.
Impact
Major degradation or frequent failure
Version or commit
Desktop 0.0.46-preview.20261006.1 (Alpha); source references at main @ 4ce6a51
Environment
macOS 27.0.1, T3 Code (Alpha) desktop 0.0.46-preview.20261006.1
Logs or stack traces
How this was established: a user noticed recurring google.com traffic from the desktop app in their firewall. I traced it to
faviconUrlForOriginin the shippedapp.asar, ran the bundle's host filter against the hosts in the table above, and confirmed the call sites and thehostname/hostmismatch on current main. Not checked: I did not capture live traffic for case B, so I haven't measured how often the Google fallback fires in practice compared with the captured desktop favicon.Workaround
Block
www.google.comfor T3 Code in a firewall (Little Snitch/LuLu). Link and preview icons fall back to the globe glyph.Possible fixes, for whoever picks this up:
encodeURIComponent(url.hostname)instead ofurl.hostinfaviconUrlForOrigin. This one-line change closes the port leak./favicon.ico(asfaviconUrlForPagealready does for tool activity) over a third-party lookup, or add a setting to disable third-party favicon lookups.