Usually at least three. A React Native app has JavaScript packages, plus iOS pods or Swift packages, plus Android Gradle libraries. A Flutter app has Dart packages in the same pattern. Each layer has its own lockfile and its own advisories, so scanning one file covers only a slice of the app.
The layers
| Layer | React Native | Flutter |
|---|---|---|
| Shared code | npm packages | Dart packages in pubspec |
| iOS native | CocoaPods or Swift packages | CocoaPods or Swift packages |
| Android native | Gradle libraries | Gradle libraries |
| Runtime engine | JavaScript engine and bridge code | Flutter engine |
Apple's third-party SDK requirements list shows how real this is. It names cross-platform pieces such as Flutter itself, Capacitor, Cordova, hermes, and many Flutter plugins like path_provider, url_launcher, image_picker_ios and shared_preferences_ios. When you add a plugin, you add native iOS and Android code behind it.
Which lockfile where
- Dart. The Dart documentation says to commit pubspec.lock for application packages, and not for regular packages.
- iOS. Podfile.lock records the exact pod versions, and the CocoaPods guide says to keep it under version control.
- Android. Gradle's lock files are
gradle.lockfileper project, if you turn locking on. - JavaScript. Commit your package manager's lockfile.
Different teams often own different files. The risk is that nobody owns the sum.
Steps for a full inventory
- List each lockfile in the repository, including those under the ios and android folders.
- Scan each one, or generate an SBOM that merges them.
- Tag each finding with its layer so the right person fixes it.
- For a vulnerable plugin, find the package it came from and check for a newer plugin version.
- Re-run on each release.
An example of the sum
Take a Flutter app with a dozen plugins. The Dart lockfile lists the plugins and their Dart dependencies. Each plugin that touches the camera, files or the network also brings Swift or Kotlin code and often pods or Gradle libraries of its own. Those native libraries are not in the Dart lockfile at all. They are resolved when the iOS and Android projects build, and they land in Podfile.lock and in the Gradle graph.
A scan that reads only the Dart file reports on the plugins. A scan that reads all three reports on the code that actually runs on the phone.
Why fixes are slower here
A vulnerable native library behind a plugin cannot be patched by editing your app. You wait for the plugin to bump the library, then update the plugin, then rebuild. Knowing which plugin pulled it in is half the work, which is what the dependency tree gives you.
For the Kotlin Multiplatform angle, see Kotlin Multiplatform supply chain risks.
Quick check
Is the JavaScript lockfile enough for a React Native app?
No. The iOS and Android layers have their own lockfiles and their own known vulnerabilities.
Should I commit pubspec.lock?
For an application, yes. Dart's documentation recommends it.
Safeguard's dependency and SBOM scanning scans dependency manifests and SBOMs, so you can point it at each layer of the repository. It does not analyze compiled mobile binaries. Try a free scan.
Sources
- Apple: third-party SDK requirements
- Dart: files to commit and not commit
- CocoaPods guides: using CocoaPods
- Gradle: dependency locking
Last reviewed: 19 October 2026.