Repository navigation
fix(shell): refuse foreign requests at the network layer on Windows; release 0.3.1 - #3
Conversation
Candidate QA of 0.3.0 on Windows 11 (WebView2 154) found that the navigation pin keeps the page in the app but not the data. location.href, location.assign, a link click and a meta refresh each sent a real navigation request (sec-fetch-mode: navigate) carrying the URL to a foreign server, even though NavigationStarting was cancelled and the app logged the refusal. wry cancels synchronously; WebView2 had already sent the request. WKWebView and WebKitGTK sent nothing in the same probe. request_pin (Windows only) adds a "*" WebResourceRequested filter on every webview as it becomes ready and answers each web request (http, https, ws, wss) to a host other than the app's asset or IPC origin with a local 403, before it leaves the machine. data:, blob: and custom schemes are local and pass; an unparseable URI is refused. wry's custom-protocol handler ignores URIs it does not own, so the two coexist. The release workflow now also runs on PRs that touch src-tauri/ or viewer-dist/: ci.yml builds on macOS only, so Windows- and Linux-only shell code otherwise first compiled at release time. webview2-com and windows were already in the lock under wry at the same versions; only the root package's dependency list changed.
0.3.0 was drafted but never published: Windows candidate QA found the navigation pin did not stop the request carrying a refused navigation's URL. 0.3.1 adds the Windows request pin.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5945617c9e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| match url.scheme() { | ||
| "http" | "https" | "ws" | "wss" => is_app_navigation(&url, allowed), | ||
| _ => true, |
There was a problem hiding this comment.
Reject network-backed file URLs
When a compromised page navigates to a URL such as file://secret.attacker.example/share/x, this branch accepts it because every scheme other than HTTP(S)/WS(S) returns true. On Windows, a hosted file:// URI is a UNC network path, and WebView2 raises WebResourceRequested for file schemes, so the handler leaves this outbound request untouched and preserves the navigation-exfiltration channel the new pin is intended to close. Restrict the pass-through list to schemes known to be non-networked, or at least reject file: URLs with a host. See the Microsoft documentation for UNC file URIs and WebView2 request interception.
Useful? React with 👍 / 👎.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
SECURITY.md and CLAUDE.md describe the WebView2 behaviour and the request pin. Release readiness records what each platform's probe measured, including the 0.3.0 Windows leak that kept that draft unpublished, and the installed-package smoke results of the candidates.
… (UNC) Codex review (P1): is_app_request passed every scheme other than http/https/ws/wss. On Windows a file://host/share URI is a UNC path, an SMB connection to that host that can also hand it the user's NTLM credentials, so a compromised page could have used it to keep the exfiltration channel open. Only data:, blob: and about: pass now; every other scheme is refused. The app's own custom protocols arrive as http://<name>.localhost on Windows, so nothing legitimate is lost. Not measured: whether WebView2 would have made the SMB connection at all (Chromium normally refuses web-origin navigations to file:). The allowlist closes it either way. The regression test (file://attacker.example/share/x, file:///C:/..., ftp:, chrome-extension:) failed before this change and passes after it.
Problem
Candidate QA of the unpublished 0.3.0 draft on Windows 11 (WebView2 154) found that the navigation pin keeps the page in the app but not the data. A compromised page can still exfiltrate:
location.href,location.assign, a link click and a meta refresh.sec-fetch-mode: navigate,sec-fetch-dest: document, no prefetch purpose) carrying the URL and query string to a foreign listener, about 10–16 ms after it was issued.http://tauri.localhost, and its stderr logged "refused a navigation away from the app".NavigationStartingsynchronously (args.SetCancel(!allow)), but WebView2 had already sent the request.window.opensends nothing.Precondition: a compromised webview, the same threat model the pin exists for. No exploit path to that is known.
Solution
On Windows, refuse every web request (http, https, ws, wss) to a host other than the app's asset origin (
http://tauri.localhost) and IPC origin (http://ipc.localhost) at the network layer. WebView2'sWebResourceRequestedis raised before a request leaves, and answering there with a local 403 means nothing reaches the network.Architecture
navigation_pinplugin gains anon_webview_readyhook, so every webview (the configured window and thedb-<n>windows) gets the request pin as it becomes ready.request_pin::install(Windows only) adds a*filter. It usesAddWebResourceRequestedFilterWithRequestSourceKindswhere available, so iframes and shared workers are covered, as wry does for its own filter. Its handler answers refused requests with a 403 built by the WebView2 environment.is_app_request, lives inlib.rsand is tested on every platform. Web schemes must target an app origin. Onlydata:,blob:andabout:pass, and every other scheme is refused,file:included. That last part comes from the Codex P1 below: on Windowsfile://host/shareis a UNC/SMB connection that can also leak NTLM credentials. An unparseable URI is refused, and the log line records only the origin.Per-file changes
src-tauri/src/request_pin.rs(new): the Windows filter and handler.src-tauri/src/lib.rs:app_request_origins,is_app_request,origin_for_log,install_request_pinand theon_webview_readyhook. Two tests: the policy, and a lockstep requiring everyconnect-srcorigin to be admitted.src-tauri/Cargo.toml,Cargo.lock:webview2-com0.38 andwindows0.61 as Windows dependencies. Both were already locked underneath wry at these versions; only the root package's dependency list changed. The notices drift check still passes..github/workflows/release.yml: PRs that touchsrc-tauri/**orviewer-dist/**now run the three-platform build.ci.ymlbuilds on macOS only, so Windows-only code like this would otherwise first compile at release time.v0.3.0.Security
This closes the Windows half of the exfiltration channel the navigation pin targets. Subresource requests to foreign hosts were already blocked by CSP; now they are also refused at the network layer on Windows.
Review findings
lib.rs:186on2eb9d96: non-web schemes pass, sofile://host/...(UNC) could slip throughtrue. Exploitability not measured; Chromium normally refuses web-origin navigations tofile:before any request.1bdbab4: an allowlist of local schemes (data:,blob:,about:). The regression (file://attacker.example/share/x,file:///C:/...,ftp:,chrome-extension:) failed before the fix and passes after it.Test plan
cargo test --lib: 138 passed, including the two new tests. Clippy clean.npm test27/27. The notices drift check passes.9776023,2eb9d96and1bdbab4.http://ipc.localhost.location.href,location.assign, link click and meta refresh; on 0.3.0 each one leaked.<base>and the form before any request.window.opensends nothing.artifacts/release-0.3.1-candidate/.