4 minutes read

Dart and Flutter

The dart detector reports Dart SDK releases that no longer receive security updates and pub packages that their publisher discontinued, as declared in pubspec.yaml, pubspec.lock, .fvmrc, and .fvm/fvm_config.json. It is supported: it is scored on the public corpus and held to the corpus precision and recall floors. Its lifecycle rules are in registry/dart.json and ship in the data bundle.

What it reads

File Declaration
pubspec.yaml environment.sdk, environment.flutter, and each direct dependencies entry that has a version constraint
pubspec.lock The resolved version of each hosted package, and the sdks.dart and sdks.flutter constraints
.fvmrc The top-level flutter pin written by FVM 3
.fvm/fvm_config.json The legacy top-level flutterSdkVersion pin. A fvm_config.json outside an .fvm directory is not read

It walks the repository up to eight directories deep (at most 500 matching files) and does not follow symbolic links. It skips .git, node_modules, vendor, dist, build, target, .venv, venv, .dart_tool, fixtures, __fixtures__, and testdata. Dart, Flutter, FVM, and pub are never run, and nothing is fetched.

What it reports

  • A Dart SDK version written exactly (sdk: 3.8.1) that falls in a release line the Dart security policy no longer covers. Dart commits security updates only for the most recent stable release, so a line loses support the day the next one ships. Reported at high severity. Bundled rules cover Dart 3.0 through 3.12 and a catch-all for Dart 2 and older.
  • A Dart SDK constraint (>=2.17.0 <3.0.0, >=3.0.0 <3.5.0) that can resolve only to end-of-support release lines, reported against the newest such line. A constraint that also admits a supported or unjudged line (^3.11.0, >=3.8.0 <4.0.0) is not reported as an old SDK.
  • A Flutter version pinned exactly in .fvmrc, .fvm/fvm_config.json, pubspec.yaml, or pubspec.lock, when a reviewed rule covers it. The registry has no Flutter rules yet, so this produces findings only after one is added.
  • A direct dependency in pubspec.yaml, or a hosted package resolved in pubspec.lock, on a pub package that the publisher marked discontinued on pub.dev, at the pubspec.yaml or pubspec.lock line that declares it.
  • Where a package rule lists usage matchers, the .dart files that import the package and the calls those matchers name, with an impact summary.

Each finding carries its rule's source, replacement, migration steps, and verification commands. The SDK rules' steps raise the pin, raise the environment.sdk constraint, and align CI; their verification commands are dart --version, dart pub get, dart analyze && flutter test, and dart analyze && dart test.

How versions are matched

  • An exact version (a resolved pubspec.lock package, an FVM pin, or a constraint written without an operator) uses the first rule whose vers range contains it. A rule whose vers is malformed is skipped, so it does not hide a later rule.
  • A Dart SDK constraint with an operator is matched only when every release line it allows is covered by an end-of-support rule; the finding uses the newest such rule.
  • A package constraint with an operator (^1.0.0) is matched only against a rule that covers every version of that package, which is how a discontinued package is recorded.
  • Packages are matched as pkg:pub/<name>, the Dart SDK as pkg:generic/dart, and the Flutter SDK as pkg:generic/flutter.

What it intentionally does not judge

  • A Dart SDK constraint that also admits a supported or unjudged line, as described above.
  • any, empty values, and ${…} templates.
  • Dependencies that use sdk: flutter, path:, git:, or hosted: without a version:.
  • dev_dependencies and dependency_overrides in pubspec.yaml. In pubspec.lock every hosted package is read, including transitive ones, because the lock file records what is resolved; git, path, and sdk sources are not.
  • FVM release channels (stable, beta), Git references, and flavor pins.
  • Vulnerability advisories, license policy, and whether a newer version of a package exists.
  • Malformed or oversized files (over 1 MiB, or a line over 4,096 characters) produce nothing, not partial results.

Known limits and false-positive boundaries

  • A discontinued package is reported at any version, because the notice is about the package, not a release. The package may still work; Offing reports that nobody is publishing it.
  • Source usage is read from .dart files only. It follows import 'package:…' and import 'dart:…' directives (including deferred as), as prefixes (prefix.target(), and names listed in show. A bare import brings unknown names into scope, so its unqualified calls are not attributed. export, part, and conditional imports (if (dart.library.io)) are not followed.
  • Nested block comments are not tracked, so code after a nested /* /* */ */ may be read as live.
  • Function.apply and, in a file that imports dart:mirrors, reflect(, reflectClass(, reflectType(, and currentMirrorSystem( are listed as unclassified rather than attributed to a package.
  • Monorepos are scanned file by file; findings in example/ or test packages are reported like any other.

Evidence

The Dart corpus holds 75 repositories: 55 selected blind (73%), and the reviewed corpus expects 138 Dart findings across 31 repositories (including Dart findings declared in other ecosystems' pins). 18 Dart-bucket repositories have no expected finding of any detector. The selection rules and counts are in Validation: Dart corpus.

View Markdown

Use with an agent
View Markdown

Open in