Skip to main content

appcraft_manifest_audit 0.0.1 copy "appcraft_manifest_audit: ^0.0.1" to clipboard
appcraft_manifest_audit: ^0.0.1 copied to clipboard

Attributes every permission and component of the merged Android manifest to the library that injected it, and freezes the answer in a baseline that fails CI when it changes.

example/README.md

Example #

A Flutter app with a camera, Firebase messaging and a media picker, after flutter build apk.

What the merged manifest holds, and who put it there #

$ dart run appcraft_manifest_audit report
debug — 6 permission(s), 3 component(s)

Permissions the app declares itself (2):
  android.permission.INTERNET                          app/src/debug, app/src/main
  android.permission.READ_EXTERNAL_STORAGE             app/src/main

Permissions injected by libraries (4):
  android.permission.ACCESS_ADSERVICES_AD_ID           androidx.privacysandbox.ads:ads-adservices
  android.permission.RECORD_AUDIO                      camera_android_camerax (via camera)
  android.permission.VIBRATE                           vibration
  android.permission.WAKE_LOCK                         com.google.firebase:firebase-messaging

In the manifest with no node in the report (1):
  android.permission.POST_NOTIFICATIONS                unattributed

Components: 3, from 4 libraries. Run "explain" for one of them.

Four permissions nobody in this project wrote. RECORD_AUDIO says via camera because camera_android_camerax is a federated implementation — the pubspec says camera, and that is the line to argue with. WAKE_LOCK names a Maven artifact rather than a plugin: the report does not say which package pulled the artifact in, and ./gradlew :app:dependencies in android/ does. INTERNET appears twice because Flutter's own debug manifest declares it for hot reload; it is not in the release variant.

Where one of them comes from #

$ dart run appcraft_manifest_audit explain RECORD_AUDIO

android.permission.RECORD_AUDIO — debug

  camera_android_camerax 0.6.19                        via camera
    android/src/main/AndroidManifest.xml:10

  Not declared in android/app/src. To drop it:
    <uses-permission android:name="android.permission.RECORD_AUDIO"
        tools:node="remove" />
  as a direct child of <manifest> in android/app/src/main/AndroidManifest.xml.

"As a direct child of <manifest>" is in the output because getting it wrong is a real and silent failure: nested inside an <activity>, the same three lines do nothing at all and nothing warns you.

Freezing the answer #

$ dart run appcraft_manifest_audit baseline
Wrote android/manifest_audit.yaml for debug. Commit it — the reviewer of that
commit is the approval mechanism.
# android/manifest_audit.yaml
version: 1
variants:
  debug:
    application-id: com.example.demo_app
    permissions:
      android.permission.ACCESS_ADSERVICES_AD_ID:
        - androidx.privacysandbox.ads:ads-adservices
      android.permission.INTERNET:
        - app/src/debug
        - app/src/main
      android.permission.RECORD_AUDIO:
        - camera_android_camerax
      android.permission.WAKE_LOCK:
        - com.google.firebase:firebase-messaging
    components:
      provider#androidx.startup.InitializationProvider:
        - androidx.startup:startup-runtime
        - firebase_core

In CI #

Add an analytics SDK, build, and the check fails:

$ flutter build apk --release --flavor prod
$ dart run appcraft_manifest_audit check --variant prod-release
prod-release: 1 new entry.

  + com.google.android.gms.permission.AD_ID            appsflyer_sdk

Review them, then run "dart run appcraft_manifest_audit baseline" and commit
the change. A permission that contradicts the Data Safety declaration is what
gets a release rejected.

Exit code 1. AD_ID is the one that matters here: an SDK declares it for you, the Data Safety form does not know about it, and the release is rejected.

Read it, then accept it:

$ dart run appcraft_manifest_audit baseline --variant prod-release

The diff in that commit is the record of the decision, and its reviewer is the approval.

Narrowing it to the variant you ship #

appcraft_manifest_audit:
  variants:
    - prod-release

Without this, check audits every variant with a report on disk — including debug, whose INTERNET and dev-dependency plugins never reach Google Play.

0
likes
150
points
59
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Attributes every permission and component of the merged Android manifest to the library that injected it, and freezes the answer in a baseline that fails CI when it changes.

Repository (GitHub)
View/report issues

Topics

#android #manifest #permissions #cli #ci

License

MIT (license)

Dependencies

args, path, xml, yaml

More

Packages that depend on appcraft_manifest_audit