Skip to content

MM-69421 - Scope server view network requests by origin and handle subframe navigation - #3872

Open
pvev wants to merge 6 commits into
masterfrom
MM-69241-local-network-block
Open

MM-69421 - Scope server view network requests by origin and handle subframe navigation#3872
pvev wants to merge 6 commits into
masterfrom
MM-69241-local-network-block

Conversation

@pvev

@pvev pvev commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a session-level request filter that scopes network requests from loaded server views to their configured origins. It also extends navigation handling to cover subframes in addition to main frame navigation.

  • Includes unit and end-to-end test coverage.

High-Level Architecture

The implementation uses two independent enforcement layers:

  1. A session-level onBeforeRequest filter that:

    • Classifies each request by its owning view.
    • Applies the configured-origin policy.
    • Covers HTTP/HTTPS, WebSocket, and worker-originated requests.
  2. Separate navigation handlers for:

    • Main-frame navigation.
    • Subframe navigation.

This ensures embedded content follows the same navigation policy as the top-level frame.

                 Loaded server view (WebContentsView)
                         │                         │
                    navigation               network request
                         │                         │
              ┌──────────┴──────────┐              ▼
              ▼                     ▼      session onBeforeRequest
       will-navigate       will-frame-navigate             │
        (main frame)           (subframes)         identify owning view
              │                     │                       │
       main-frame policy     subframe policy        ┌───────┴───────┐
                                                 configured?       other
                                                     │              │
                                                   allow          apply
                                                               origin policy

Ticket Link

https://mattermost.atlassian.net/browse/MM-69241

Checklist

  • [ x] Added or updated unit tests (required for all new features)
  • [ x] Has UI changes
  • [x ] read and understood our Contributing Guidelines
  • [x ] completed Mattermost Contributor Agreement
  • [ x] executed npm run lint:js for proper code formatting
  • [ x] Run E2E tests by adding label E2E/Run

Device Information

Apple M4 MAX 64 GB Tahoe

Release Note

Improved how network requests and embedded content from connected servers are handled, keeping requests scoped to your configured servers

Change Impact: Medium 🟡

Regression Risk: Medium likelihood of security/policy regressions because the change introduces a session-wide webRequest.onBeforeRequest cancellation filter and extends navigation gating to will-frame-navigate, impacting HTTP/HTTPS, WebSocket, redirects, and service-worker/worker-originated requests; coverage includes both unit tests and a dedicated local-network E2E spec, but behavior could still differ across Electron view ownership/webContents contexts.

QA Recommendation: Limited manual QA recommended: verify in an Electron run that (1) configured-origin requests still load, (2) local/private targets are blocked (including subframe embeds, redirects, and service-worker fetch behavior), and (3) subframe navigation handling matches expectations for about:blank/about:srcdoc while disallowed protocols are prevented.
Generated by CodeRabbitAI

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 218fea58-346d-46c8-a9b5-96e3d61b1c4a

📥 Commits

Reviewing files that changed from the base of the PR and between 50a37f3 and 1ec92b1.

📒 Files selected for processing (3)
  • src/main/app/initialize.ts
  • src/main/security/localNetworkAccess.test.ts
  • src/main/security/localNetworkAccess.ts

📝 Walkthrough

Walkthrough

The PR adds main-process local-network request filtering, subframe navigation allowlists, and end-to-end coverage using loopback servers and Electron policy tests.

Changes

Local network access policy

Layer / File(s) Summary
URL and address blocking
src/main/security/localNetworkAccess.ts, src/main/security/localNetworkAccess.test.ts
Protocol filtering, server-origin normalization, hostname resolution, and IPv4/IPv6 private-address classification are implemented and tested.
Request gate and server-owned bypass
src/main/security/localNetworkAccess.ts, src/main/security/localNetworkAccess.test.ts
shouldCancelLocalNetworkRequest derives server origins and webContents ownership internally before deciding whether to cancel requests.
Session request filter
src/main/app/initialize.ts, src/main/app/initialize.test.js
The main process registers defaultSession.webRequest.onBeforeRequest to cancel blocked requests and allow requests when policy evaluation throws.
Subframe navigation guard
src/app/views/webContentEventsCommon.ts, src/app/views/webContentEvents.ts, src/app/views/webContentEventsCommon.test.ts, src/app/views/webContentEvents.test.js
Subframe navigation allowlists and will-frame-navigate listener wiring support http, https, about:blank, and about:srcdoc, while blocking other protocols.
Local-network test harness
e2e/helpers/config.ts, e2e/helpers/localNetworkServers.ts, e2e/specs/policy/local_network.test.ts
Loopback fake and secret servers, local-network configuration, and renderer helpers for fetch, iframe, and service-worker access are added.
Local-network e2e cases
e2e/specs/policy/local_network.test.ts
The e2e spec checks blocked direct requests, configured-origin access, blocked subframes and redirects, and blocked service-worker fetches.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Renderer
  participant ElectronSession
  participant NetworkPolicy
  participant LocalServer
  Renderer->>ElectronSession: fetch, frame, redirect, or service-worker request
  ElectronSession->>NetworkPolicy: evaluate URL and webContents context
  NetworkPolicy->>LocalServer: resolve or allow configured-origin request
  LocalServer-->>NetworkPolicy: response or resolved address
  NetworkPolicy-->>ElectronSession: cancel or allow
  ElectronSession-->>Renderer: blocked request or response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: origin-scoped server view requests and subframe navigation handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MM-69241-local-network-block

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the E2E/Run Run Desktop E2E Tests label Jun 26, 2026
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Documentation Impact Analysis — updates needed

Documentation Impact Analysis

Overall Assessment: Documentation Updates Recommended

Changes Summary

PR #3872 adds a session-level onBeforeRequest request filter that blocks network requests from loaded server views from reaching local/private network addresses (loopback, RFC-1918, link-local, IPv6 ULA) while continuing to allow requests to configured Mattermost server origins. It also adds a will-frame-navigate handler that controls subframe (iframe) navigation by blocking non-web protocols. The changes are automatic with no new configuration knobs but represent a significant, observable security policy shift affecting how embedded content and integrations interact with the host network.

Documentation Impact Details

Change Type Files Changed Affected Personas Documentation Action Docs Location
New security behavior — local/private network access policy src/main/security/localNetworkAccess.ts (new), src/main/app/initialize.ts IT / Security Officer, System Administrator Document new automatic policy: server view requests to local/private IPs are blocked except for configured Mattermost server origins; includes redirect and WebSocket coverage New section in docs/source/deployment-guide/desktop/desktop-app-deployment.rst or new page docs/source/deployment-guide/desktop/desktop-network-access-policy.rst
New subframe navigation policy — non-web protocol iframes blocked src/app/views/webContentEvents.ts, src/app/views/webContentEventsCommon.ts End User, IT / Security Officer Document that iframes using non-http(s) protocols (tel:, mailto:, custom://, file://) are now blocked; http/https and about:blank/about:srcdoc continue to load docs/source/deployment-guide/desktop/desktop-troubleshooting.rst (troubleshooting note)
Desktop app changelog — v6.2.0 entry missing this change PR targets v6.2.0 milestone All personas Add release note entry describing the new network isolation behavior docs/source/product-overview/desktop-app-changelog.md under the existing ## Release v6.2 section

Recommended Actions

  • Add a "Network access policy" section to docs/source/deployment-guide/desktop/desktop-app-deployment.rst (or create docs/source/deployment-guide/desktop/desktop-network-access-policy.rst and link from the deployment index) explaining: (1) server view requests to local/private IP ranges are automatically blocked; (2) configured Mattermost server origins — including self-hosted servers on loopback or private IPs — are always allowed; (3) WebSocket (ws:/wss:) connections follow the same policy as HTTP; (4) redirects from the configured server to a local address are also blocked.
  • Add a troubleshooting entry to docs/source/deployment-guide/desktop/desktop-troubleshooting.rst covering the symptom "an embedded iframe or integration content stopped loading after upgrading" and explaining that iframes using custom/file/tel/mailto URI schemes are now blocked, while http/https embeds continue to work.
  • Update docs/source/product-overview/desktop-app-changelog.md under ## Release v6.2 > v6.2.0 to include the release note: "Improved how network requests and embedded content from connected servers are handled, keeping requests scoped to your configured servers."

Confidence

Medium — The PR carries a Docs/Needed label (confirming the team assessed that documentation is required) and the new behavior has no admin-facing toggle, making it purely a behavioral change. The main uncertainty is whether the network access policy warrants a new standalone page vs. a section within the existing deployment guide; the recommended action calls for whichever structure fits the docs team's preference.


@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/app/views/webContentEvents.test.js (1)

133-198: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add one regression case for the popup-specific subframe listener.

This suite only exercises addWebContentsEventListeners(), but src/app/views/webContentEvents.ts also wires will-frame-navigate directly on popup webContents in the plugin/managed-resource branch. A small test for that branch would keep this security-sensitive path from regressing unnoticed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/views/webContentEvents.test.js` around lines 133 - 198, Add a
regression test for the popup-specific `will-frame-navigate` listener path,
since `addWebContentsEventListeners()` is not the only place this guard is
attached. Extend the `WebContentsEventManager` coverage to exercise the
popup/plugin/managed-resource branch in `webContentEvents.ts`, verify the
listener is registered on popup `webContents`, and assert the same main-frame
bypass and protocol-blocking behavior so this security-sensitive path stays
covered.
src/main/security/localNetworkAccess.ts (1)

88-104: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Coalesce DNS checks on the request hot path.

This helper is awaited from the session onBeforeRequest handler, so every repeated request to the same public hostname currently pays for a fresh dns.lookup() before Chromium can continue. That is an avoidable latency hit for CDN/embed-heavy pages. Cache or at least dedupe in-flight lookups by normalized hostname here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/security/localNetworkAccess.ts` around lines 88 - 104, The
isLocalOrPrivateHostname helper currently performs a fresh dns.lookup through
lookup for every repeated public hostname on the onBeforeRequest hot path,
adding avoidable latency. Add caching or in-flight deduplication keyed by the
normalized hostname inside isLocalOrPrivateHostname so repeated calls reuse the
same result. Keep the existing local/private fast-path checks in
normalizeHostname, isLocalhostHostname, and isLocalOrPrivateIPAddress unchanged,
and ensure lookup failures still fall back to false.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/helpers/localNetworkServers.ts`:
- Around line 52-88: startLocalNetworkServers leaves secretService running if
listen(fakeServer) fails after the first server starts, which can leak a bound
loopback listener into later tests. Update startLocalNetworkServers to wrap the
startup sequence in try/catch and ensure any successfully started server is
closed on failure, reusing the existing close/cleanup pattern from
LocalNetworkServers. Make sure both secretService and fakeServer are tracked so
partial startup is always cleaned up before rethrowing.

In `@src/app/views/webContentEvents.ts`:
- Around line 38-41: The import block in webContentEvents should follow the
repo’s singleton and ordering conventions: rename the allowProtocolDialog import
to PascalCase and keep the relative imports grouped and alphabetized according
to the enforced ESLint order. Update the import section in webContentEvents to
use the correct symbol name for the singleton and preserve the existing grouping
around generateHandleConsoleMessage, isAllowedSubframeNavigation,
isCustomProtocol, isMattermostProtocol, composeUserAgent, and
allowProtocolDialog.

In `@src/main/app/initialize.test.js`:
- Around line 333-340: The helper in initialize.test.js is reading the first
onBeforeRequest registration instead of the one created by its own initialize()
call. Update getRegisteredHandler to return the most recently registered handler
from session.defaultSession.webRequest.onBeforeRequest.mock.calls so it always
uses the handler added by that helper, even when initialize() was called earlier
in the file.

In `@src/main/app/initialize.ts`:
- Around line 361-366: The warning in initialize.ts is logging the full
attacker-controlled URL via the shouldCancel block, which can leak query strings
and intranet paths. Update the log payload in the blocked-request path to redact
details.url before it is emitted, keeping only the origin/host or another
scrubbed form while preserving the other fields like details.resourceType and
details.webContentsId.

---

Nitpick comments:
In `@src/app/views/webContentEvents.test.js`:
- Around line 133-198: Add a regression test for the popup-specific
`will-frame-navigate` listener path, since `addWebContentsEventListeners()` is
not the only place this guard is attached. Extend the `WebContentsEventManager`
coverage to exercise the popup/plugin/managed-resource branch in
`webContentEvents.ts`, verify the listener is registered on popup `webContents`,
and assert the same main-frame bypass and protocol-blocking behavior so this
security-sensitive path stays covered.

In `@src/main/security/localNetworkAccess.ts`:
- Around line 88-104: The isLocalOrPrivateHostname helper currently performs a
fresh dns.lookup through lookup for every repeated public hostname on the
onBeforeRequest hot path, adding avoidable latency. Add caching or in-flight
deduplication keyed by the normalized hostname inside isLocalOrPrivateHostname
so repeated calls reuse the same result. Keep the existing local/private
fast-path checks in normalizeHostname, isLocalhostHostname, and
isLocalOrPrivateIPAddress unchanged, and ensure lookup failures still fall back
to false.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d9712224-8096-4610-aec8-25fed4e326cd

📥 Commits

Reviewing files that changed from the base of the PR and between 098f4d0 and b808e66.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • e2e/helpers/config.ts
  • e2e/helpers/localNetworkServers.ts
  • e2e/specs/policy/local_network.test.ts
  • src/app/views/webContentEvents.test.js
  • src/app/views/webContentEvents.ts
  • src/app/views/webContentEventsCommon.test.ts
  • src/app/views/webContentEventsCommon.ts
  • src/main/app/initialize.test.js
  • src/main/app/initialize.ts
  • src/main/security/localNetworkAccess.test.ts
  • src/main/security/localNetworkAccess.ts

Comment thread e2e/helpers/localNetworkServers.ts Outdated
Comment thread src/app/views/webContentEvents.ts
Comment thread src/main/app/initialize.test.js
Comment thread src/main/app/initialize.ts
@github-actions github-actions Bot removed the E2E/Run Run Desktop E2E Tests label Jun 26, 2026
@github-actions github-actions Bot added the E2E/Run Run Desktop E2E Tests label Jun 26, 2026
@github-actions github-actions Bot removed the E2E/Run Run Desktop E2E Tests label Jun 26, 2026
@pvev
pvev requested review from devinbinnie and edgarbellot June 26, 2026 16:09
@pvev pvev added 2: Dev Review Requires review by a core committer 3: Security Review Review requested from Security Team labels Jun 26, 2026

@edgarbellot edgarbellot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, happy path + edge cases perfectly covered 🚀 thank you!

@edgarbellot edgarbellot removed the 3: Security Review Review requested from Security Team label Jun 29, 2026
@devinbinnie devinbinnie added this to the v6.2.0 milestone Jul 16, 2026

@devinbinnie devinbinnie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work here @pvev, I think I have a few worthwhile improvements we can make to make this more correct. We'll probably want @edgarbellot to re-review afterwards.

Comment thread src/main/app/initialize.ts Outdated
Comment thread src/main/app/initialize.ts Outdated
webContentsId: details.webContentsId,
webContents: details.webContents,
},
ServerManager.getAllServers().map((server) => server.url),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method that we're calling can pull these values in themselves, we don't need to pass them in from here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this so the request details are passed through directly and the helper owns the extraction logic.

Comment thread src/app/views/webContentEvents.ts Outdated

const log = new Logger('WebContentsEventManager');

type NavigationEvent = Event<WebContentsWillNavigateEventParams | WebContentsWillFrameNavigateEventParams>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this type? The events are different and are shaped differently. Since we're creating different functions for each event type, we don't need to combine the types here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, removed the combined type and typed the handlers independently.

Comment thread src/app/views/webContentEvents.ts Outdated
Comment thread src/main/security/localNetworkAccess.ts Outdated
return false;
}

function isLocalOrPrivateIPv4(address: string): boolean {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementations for checking specific IPv4 and IPv6 addresses here and below feel very janky and one thing I think we should be avoiding is bitwise operations in JS.

I checked with Claude and found a potential alternative that seems cleaner even if it's not perfect, it uses Node's net.BlockList object that allows us to specify CIDRs we want to block on and check those. Here's an example:

import {BlockList, isIP} from 'net';

// Local, loopback, and private ranges we refuse to let server content reach.
// Declarative CIDR data; matching is handled natively by BlockList.
const LOCAL_NETWORK_BLOCKLIST = new BlockList();

// IPv4
LOCAL_NETWORK_BLOCKLIST.addSubnet('0.0.0.0', 8, 'ipv4');      // unspecified / "this network"
LOCAL_NETWORK_BLOCKLIST.addSubnet('10.0.0.0', 8, 'ipv4');     // RFC 1918
LOCAL_NETWORK_BLOCKLIST.addSubnet('127.0.0.0', 8, 'ipv4');    // loopback
LOCAL_NETWORK_BLOCKLIST.addSubnet('169.254.0.0', 16, 'ipv4'); // link-local
LOCAL_NETWORK_BLOCKLIST.addSubnet('172.16.0.0', 12, 'ipv4');  // RFC 1918
LOCAL_NETWORK_BLOCKLIST.addSubnet('192.168.0.0', 16, 'ipv4'); // RFC 1918

// IPv6
LOCAL_NETWORK_BLOCKLIST.addSubnet('::1', 128, 'ipv6');        // loopback
LOCAL_NETWORK_BLOCKLIST.addSubnet('fc00::', 7, 'ipv6');       // unique local
LOCAL_NETWORK_BLOCKLIST.addSubnet('fe80::', 10, 'ipv6');      // link-local

export function isLocalOrPrivateIPAddress(address: string): boolean {
    const family = isIP(address);
    if (family === 4) {
        return LOCAL_NETWORK_BLOCKLIST.check(address, 'ipv4');
    }
    if (family === 6) {
        // Passing 'ipv6' lets BlockList match IPv4-mapped addresses
        // (::ffff:127.0.0.1) against the IPv4 rules above automatically.
        return LOCAL_NETWORK_BLOCKLIST.check(address, 'ipv6');
    }
    return false;
}

We should confirm with Prod Sec, but I think this would be a cleaner way to do this. Avoiding rolling our own IP detection here would be preferred.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, replaced the custom IP range parsing with Node’s BlockList and kept the existing coverage for the expected ranges.

@devinbinnie devinbinnie added the CherryPick/Approved Meant for the quality or patch release tracked in the milestone label Jul 16, 2026
@devinbinnie

Copy link
Copy Markdown
Member

FYI this needs to be cherry-picked to v6.3 as well.

…nts handling while preserving request policy coverage
@pvev
pvev requested review from devinbinnie and edgarbellot July 22, 2026 11:36

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/security/localNetworkAccess.ts`:
- Around line 32-34: Update the IPv6 initialization in LOCAL_NETWORK_BLOCKLIST
to add the omitted fec0::/10 site-local subnet, and add a regression assertion
verifying addresses in that range are blocked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: db9cc64c-cf30-457c-86af-678122b224c5

📥 Commits

Reviewing files that changed from the base of the PR and between eb8eec4 and edcce10.

📒 Files selected for processing (6)
  • e2e/specs/policy/local_network.test.ts
  • src/app/views/webContentEvents.ts
  • src/main/app/initialize.test.js
  • src/main/app/initialize.ts
  • src/main/security/localNetworkAccess.test.ts
  • src/main/security/localNetworkAccess.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/app/initialize.test.js
  • e2e/specs/policy/local_network.test.ts
  • src/main/app/initialize.ts
  • src/app/views/webContentEvents.ts

Comment thread src/main/security/localNetworkAccess.ts
@pvev

pvev commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

DryRun Security

This pull request introduces a low-severity DNS rebinding vulnerability in the isLocalOrPrivateHostname function, where the non-atomic nature of the DNS check and subsequent network request could allow an attacker to bypass SSRF protections. Although the issue is flagged as risky, it is not currently set to block the merge.

DNS Rebinding in Hostname Validation in src/main/security/localNetworkAccess.ts (drs_026233b0)

Vulnerability DNS Rebinding in Hostname Validation
Description The isLocalOrPrivateHostname function performs a DNS lookup to check if a hostname resolves to a private IP address. However, this check is performed independently of the actual network request made by the Electron application. Because the check and the subsequent network request are not atomic, an attacker could potentially use DNS rebinding (e.g., by providing a short TTL for the DNS record) to cause the check to pass for a benign IP address while the actual request is made to a private or loopback IP address. This is a known limitation of using webRequest.onBeforeRequest for SSRF protection without pinning the IP address or using other network-level mitigations.

return addresses.some(({address}) => isLocalOrPrivateIPAddress(normalizeHostname(address)));
} catch {
return false;
}

Comment to provide feedback on these findings.
Report false positive: @dryrunsecurity fp [FINDING ID] [FEEDBACK] Report low-impact: @dryrunsecurity nit [FINDING ID] [FEEDBACK]

Example: @dryrunsecurity fp drs_90eda195 This code is not user-facing

All finding details can be found in the DryRun Security Dashboard.

@DryRunSecurity nit drs_026233b0 This is a valid known limitation of doing hostname classification before Chromium performs the actual request resolution. It is not introduced by the BlockList change, and Electron does not expose the final resolved IP in onBeforeRequest, so closing this fully would require a different network-layer design. It is safe to ignore and out of the scope of this change.

@github-actions github-actions Bot added the E2E/Run Run Desktop E2E Tests label Jul 22, 2026

@devinbinnie devinbinnie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pvev, huge improvement. I have two more concerns then we should be good to go.

Comment thread src/main/app/initialize.ts Outdated
try {
const shouldCancel = await shouldCancelLocalNetworkRequest(
details,
ServerManager.getAllServers().map((server) => server.url),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, what I was saying before is that ServerManager.getAllServers().map((server) => server.url) can be called from shouldCancelLocalNetworkRequest. Actually, same with the function that gets the view by webcontentsid.

This call should just pass in the details (which I believe contains the webcontentsid as well)


const defaultLookup: LookupFunction = (hostname: string) => dns.lookup(hostname, {all: true, verbatim: true});

const LOCAL_NETWORK_BLOCKLIST = new BlockList();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe a quick comment here explaining what this list is (ie. local IP addresses)

@github-actions github-actions Bot removed the E2E/Run Run Desktop E2E Tests label Jul 22, 2026
@amyblais amyblais added the 3: Security Review Review requested from Security Team label Jul 23, 2026 — with Claude
@edgarbellot
edgarbellot requested review from edgarbellot and hmohammed-prodsec and removed request for edgarbellot and hmohammed-prodsec July 28, 2026 06:43
@edgarbellot

Copy link
Copy Markdown

Removing myself from the reviewers list since I still see there are pending changes. Please add me again after Devin has approved everything and I'll re-review :)

cc @pvev @devinbinnie

@edgarbellot
edgarbellot removed their request for review July 28, 2026 07:21
@github-actions github-actions Bot added the E2E/Run Run Desktop E2E Tests label Jul 30, 2026
@pvev
pvev requested a review from devinbinnie July 30, 2026 08:15
@dryrunsecurity

Copy link
Copy Markdown

DryRun Security

This pull request introduces a low-severity DNS rebinding vulnerability in the shouldCancelLocalNetworkRequest function due to a Time-of-Check to Time-of-Use (TOCTOU) issue. The flaw allows an attacker to potentially bypass the local network access security filter by manipulating DNS resolution between the check and the actual network request.

DNS Rebinding in src/main/security/localNetworkAccess.ts (drs_508aff52)
Vulnerability DNS Rebinding
Description The shouldCancelLocalNetworkRequest function performs a DNS lookup to check if a hostname resolves to a private IP address. This creates a Time-of-Check to Time-of-Use (TOCTOU) vulnerability because the DNS resolution performed in the check is independent of the actual network request made by the Electron network stack. An attacker could potentially use DNS rebinding to resolve the hostname to a public IP during the check and to a private IP during the actual request, bypassing the security filter.

return addresses.some(({address}) => isLocalOrPrivateIPAddress(normalizeHostname(address)));
} catch {
return false;
}


Comment to provide feedback on these findings.

Report false positive: @dryrunsecurity fp [FINDING ID] [FEEDBACK]
Report low-impact: @dryrunsecurity nit [FINDING ID] [FEEDBACK]

Example: @dryrunsecurity fp drs_90eda195 This code is not user-facing

All finding details can be found in the DryRun Security Dashboard.

@pvev

pvev commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@DryRunSecurity nit drs_508aff52 . This is a partially valid low severity residual limitation of the pre-request DNS check, rather than a new vulnerability introduced by the PR. Fully addressing it requires connection level enforcement or Chromium’s native Local Network Access controls. I suggest accepting this as a residual very low risk for this fix and perhaps tracking native enforcement separately, this to avoid introducing a high regression custom networking layer completely out of scope of this change.

@dryrunsecurity

Copy link
Copy Markdown

Finding 508aff52 has been successfully dismissed and marked as Won't Fix / Nitpick.

@github-actions github-actions Bot removed the E2E/Run Run Desktop E2E Tests label Jul 30, 2026

@devinbinnie devinbinnie left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pvev :) LGTM

@devinbinnie
devinbinnie requested a review from edgarbellot July 30, 2026 12:38
@devinbinnie devinbinnie removed the 2: Dev Review Requires review by a core committer label Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3: Security Review Review requested from Security Team CherryPick/Approved Meant for the quality or patch release tracked in the milestone Docs/Needed release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants