WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source

ID

dart.platform.unrestricted_webview_load

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Platform

Language

Dart

Description

A WebView loads HTML or a URL from a non-literal value (loadHtmlString($X), loadRequest(Uri.parse($X)), loadFile($P), loadData(data: $H), loadUrl(urlRequest: URLRequest(url: WebUri($U))), InAppWebViewInitialData(data: $H)). If the value flows from the network, deeplink, or user input, the WebView renders attacker- controlled content.

Rationale

WebView load APIs are HTML / URL sinks — anything reaching their first argument is treated as a document to render. When that argument is a variable (rather than a string literal), it is trivially reachable from:

  • A deeplink query parameter that the app passes through to loadHtmlString without sanitization.

  • An HTTP response body interpolated into a template.

  • A push-notification payload.

  • File uploads from another app via share-intent.

Once attacker HTML renders inside the WebView, the impact stacks with webview_javascript_enabled (XSS in WebView origin) and webview_file_access_enabled (local file exfiltration). Even with JS disabled, attacker HTML can stage credential phishing UIs that look identical to the app’s own screens.

The following code illustrates a vulnerable pattern detected by this rule:

class LoadUntrusted {
  void wvf(WebViewController controller, String html, String url, String path) {
    // VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
    controller.loadHtmlString(html);
    // VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
    controller.loadHtmlString(html, baseUrl: 'https://api.example.com');
    // VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
    controller.loadRequest(Uri.parse(url));
    // VULNERABLE: WebView loads HTML or URL from a non-literal (potentially attacker-controlled) source
    controller.loadFile(path);
  }
}

Remediation

Prefer loadFlutterAsset(…​) for bundled HTML, fully-qualified HTTPS URLs hardcoded in source for known-trusted endpoints, and strict sanitization (allow-listed tags / attributes) at the boundary if user content must reach a WebView.

// Vulnerable: untrusted value reaches the WebView
controller.loadHtmlString(html);                       // html from network
controller.loadRequest(Uri.parse(url));                 // url from deeplink
controller.loadData(data: htmlFromUser);

// Fixed: bundled asset (no untrusted input reaches the document)
await controller.loadFlutterAsset('assets/help.html');

// Fixed: hardcoded known-trusted URL
controller.loadRequest(Uri.parse('https://docs.example.com/help'));

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved