Deeplink / URI query parameter consumed without validation (forced unwrap)

ID

dart.platform.deeplink_unvalidated

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Platform

Language

Dart

Description

A deeplink / URI parameter is consumed with a forced unwrap (!) and no visible validation: uri.queryParameters['k']!, state.uri.queryParameters['k']!, Uri.parse(link!), .fragment!. An attacker who triggers the deeplink with crafted parameters can crash the app, drive open-redirect behaviour, or push unvalidated values into downstream sinks.

Rationale

Custom URI schemes, App Links, Universal Links, and notification deeplinks are an open-to-the-world entry point: any app, web page, or pasted link on the device can trigger them. Forced-unwrapping the query / path parameter (! after queryParameters[..]) is a signal that the developer treats the input as "obviously present" — but the attacker decides what’s in the URI, so the value can be null, empty, or arbitrarily long.

Concrete impacts:

  • Crash / DoS: Null check operator used on a null value if the parameter is missing; can be triggered by any external app.

  • Open redirect / phishing: the param is used as the target of a Navigator push, launchUrl, or WebView load (combine with unrestricted_webview_load).

  • State manipulation: the param is passed to a constructor that sets app state (transfer amount, recipient, plan tier) without further validation.

The rule narrows to forced-unwrap because that idiom strongly signals trust without validation. ? / ?? / Uri.tryParse are the safe alternatives.

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

class DeepLinks {
  void uriQuery(Uri uri) {
    // VULNERABLE: Deeplink / URI query parameter consumed without validation (forced unwrap)
    final next = uri.queryParameters['next']!;
    // VULNERABLE: Deeplink / URI query parameter consumed without validation (forced unwrap)
    final mode = uri.queryParametersAll['mode']!;
    // VULNERABLE: Deeplink / URI query parameter consumed without validation (forced unwrap)
    final id = uri.pathParameters['id']!;
    // VULNERABLE: Deeplink / URI query parameter consumed without validation (forced unwrap)
    final frag = uri.fragment!;
    print('$next $mode $id $frag');
  }
}

Remediation

Validate every deeplink parameter against an explicit allow-list / type-conversion before use. Use ?? for nullable defaults rather than ! to force-unwrap. For navigation, never push to a route derived from a deeplink without a hardcoded allow-list.

// Vulnerable
final id = state.pathParameters['id']!;             // crashes if missing
final url = uri.queryParameters['next']!;           // open redirect sink
final u = Uri.parse(link!);                          // crashes on null

// Fixed
final id = state.pathParameters['id'] ?? '';
final url = uri.queryParameters['next'] ?? '/';
if (!_safeRedirectAllowList.contains(url)) return;
final u = Uri.tryParse(link ?? '');
if (u == null || !_acceptedSchemes.contains(u.scheme)) return;

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved