Dart code loaded from a remote URI or reflected over via dart:mirrors

ID

dart.code.code_injection

Severity

high

Remediation Complexity

hard

Remediation Risk

high

Remediation Effort

medium

Resource

Code

Language

Dart

Description

Dart code is loaded from a remote URI at runtime (Isolate.spawnUri(Uri.parse('http(s)://…​'))) or reflected over via dart:mirrors. Both bypass the integrity guarantees of the shipped binary and let an attacker control the executing code.

Rationale

Two distinct anti-patterns:

  • Isolate.spawnUri with a remote URI: spawns a new Dart isolate from code fetched over the network. An attacker who can serve the URI (DNS spoofing, captive portal, server compromise) runs arbitrary Dart with the app’s full privileges. Even an HTTPS URL is dangerous if the certificate is not pinned. Bundled-asset URIs and package: URIs are safe because the code ships with the app and is signed with the rest of the binary.

  • dart:mirrors: enables runtime metaprogramming. It is unsupported in Flutter AOT builds (flutter build apk/ipa), but apps that include it in source signal a non-standard runtime path and may be vulnerable to reflection-based bypasses of access controls (mirror.invoke(privateMethod)) that the type system would otherwise prevent. Worth flagging for audit.

Combined, these patterns let untrusted bytes become running code — the textbook definition of CWE-94 (code injection).

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

// Positive cases for dart.code_injection.

import 'dart:isolate';
// VULNERABLE: Dart code loaded from a remote URI or reflected over via dart:mirrors
import 'dart:mirrors';

Remediation

Bundle all Dart code at build time. Do not download executable code at runtime; for data-driven UI changes use config files (JSON / YAML loaded as data, not eval’d). Remove dart:mirrors imports from Flutter apps.

// Vulnerable
await Isolate.spawnUri(Uri.parse('https://example.com/code.dart'), [], null);
import 'dart:mirrors';   // unusual in Flutter apps

// Fixed: bundled code only
await Isolate.spawn(workerEntry, port.sendPort);

// Fixed: data-driven config — load JSON, never eval
final config = jsonDecode(await rootBundle.loadString('assets/config.json'));

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved