How to Secure Navigation and External Links in WebView-Enabled Native SDK Apps
Validate every URL before calling navigate(), intercept external link clicks inside the WebView, and never enable the trusted flag for remote content.
The Vercel Native SDK lets you embed multiple WebViews inside a native window using handles that expose native-side controls. Because the SDK itself does not enforce URL restrictions, securing navigation and external links in WebView-enabled Native SDK apps falls entirely to the developer. This guide maps the actual type definitions and options in vercel-labs/native to concrete security patterns you can implement today.
Understand the WebView Security Model in the Native SDK
The public API surface lives in packages/native-sdk/native-sdk.d.ts. It defines NativeSdkWebViewHandle, which exposes a navigate(url: string) method (around line 128 of the declaration file) that triggers navigation without any built-in allow-list. The SDK also declares:
NativeSdkCreateWebViewOptions– configuration passed toNativeSdk.webviews.create(), including atrustedflag that controls whetherwindow.zerois injected.NativeSdkWebViewInfo– metadata payload that reflects the WebView’s current URL after navigation.NativeSdkSetWebViewLayerOptions– z-index controls used to stack WebViews relative to native UI layers.
According to docs/src/lib/site.ts, the project treats WebViews as an opt-in feature, while docs/src/lib/page-titles.ts maps the human-readable documentation titles and docs/src/lib/docs-navigation.ts organizes the navigation structure. None of these files implement runtime security, confirming that policy enforcement belongs in host application code.
Validate URLs Before Calling navigate()
The first line of defense is a strict validation routine invoked before any call to navigate(). The SDK typings define the method signature as navigate(url: string), accepting any string you provide.
Enforce an explicit allow-list that covers scheme, hostname, and path:
const allowedHosts = new Set(['example.com', 'cdn.example.com']);
function isSafeUrl(raw: string): boolean {
try {
const url = new URL(raw);
return url.protocol === 'https:' && allowedHosts.has(url.hostname);
} catch {
return false;
}
}
Only proceed with navigation if isSafeUrl returns true:
if (isSafeUrl(targetUrl)) {
await webview.navigate(targetUrl);
} else {
throw new Error('Blocked navigation to disallowed URL');
}
Intercept External Links Inside the WebView
Remote pages can contain <a> tags that point to phishing sites or unauthorized domains. Since the SDK does not auto-intercept these, inject a click handler into the WebView context to inspect each link before it loads.
For untrusted destinations, prevent the default behavior and offload the URL to the system browser via NativeSdk.shell.openExternal:
webview.addEventListener('dom-ready', () => {
webview.executeJavaScript(`
document.addEventListener('click', (e) => {
const a = e.target.closest('a');
if (!a) return;
const href = a.href;
if (!window.__isSafeUrl(href)) {
e.preventDefault();
window.__openExternal(href);
}
});
`);
});
// Inject helpers into the WebView context
webview.executeJavaScript(`
window.__isSafeUrl = ${isSafeUrl.toString()};
window.__openExternal = (url) => {
NativeSdk.shell.openExternal(url);
};
`);
This pattern keeps the user inside the embedded view for approved content while routing external links out to the OS browser.
Use the Trusted Flag Only for Internal UI
The trusted option in NativeSdkCreateWebViewOptions determines whether the SDK injects a privileged window.zero object into the page. As implemented in vercel-labs/native, this capability is intended for first-party app chrome only.
- Set
trusted: falsefor any WebView that loads remote or user-generated content. - Reserve
trusted: truefor local HTML assets that constitute your own trusted interface.
Enabling privileged injection on untrusted sites gives remote scripts unrestricted access to the native host, which defeats the sandbox.
Layer Untrusted WebViews Behind Native UI Controls
The SDK exposes layer controls through NativeSdk.webviews.setLayer(), typed as NativeSdkSetWebViewLayerOptions. When you must render untrusted content, place the WebView on a lower layer and overlay native UI controls above it.
await NativeSdk.webviews.setLayer({
label: 'secure-child',
layer: 10,
});
Place dismissal buttons, address bars, or confirmation dialogs on a higher layer. This ensures user actions can be intercepted by your code before reaching the underlying WebView.
Monitor Navigation with WebView Metadata
After calling navigate(), verify the outcome by inspecting the NativeSdkWebViewInfo payload, which includes the WebView’s current URL. If the reported location deviates from the intended destination, close the view or reset the stack.
The documentation files docs/src/lib/docs-navigation.ts, docs/src/lib/page-titles.ts, and docs/src/lib/site.ts frame WebViews as an opt-in capability. This design reinforces the expectation that developers implement custom policies around navigation rather than relying on a built-in browser sandbox.
Summary
navigate(url)does not filter destinations; always validate URLs with a scheme and hostname allow-list before invoking it.- Inject click interceptors inside the WebView to catch
<a>tags and route disallowed links toNativeSdk.shell.openExternal. - Keep
trusted: falsefor every WebView that renders remote content to prevent privilegedwindow.zeroinjection. - Use
NativeSdk.webviews.setLayer()to stack untrusted WebViews beneath native UI overlays that can block or dismiss them. - Inspect
NativeSdkWebViewInfometadata after navigation to confirm the final URL matches your expectations.
Frequently Asked Questions
How does the Native SDK enforce URL security for WebViews?
It does not. The navigate() method and WebView creation options defined in packages/native-sdk/native-sdk.d.ts accept arbitrary strings. The SDK delegates all URL policy decisions to the host application.
What is the risk of setting trusted to true on a remote WebView?
Setting trusted: true injects a privileged window.zero object into the page context. According to the Native SDK type definitions, this gives the loaded page elevated access to the native host, which is dangerous for any content you do not control.
How should external links inside a WebView be handled?
Intercept click events on <a> elements inside the WebView, validate the href against your allow-list, and call e.preventDefault() for disallowed URLs. Then invoke NativeSdk.shell.openExternal to send those links to the system browser instead of loading them inside the embedded view.
Can I rely on layer ordering to prevent malicious WebView behavior?
Layer ordering is a defense-in-depth measure, not a primary security boundary. Use NativeSdkSetWebViewLayerOptions to place native UI controls above untrusted WebViews, but pair this with URL validation and the trusted: false flag to maintain a proper sandbox.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →