flutter_artifact_lint
flutter_artifact_lint scans Flutter iOS build artifacts before they move to signing, upload, or release.
The first target is the unsigned app generated by:
flutter build ios --release --no-tree-shake-icons --no-codesign
Then run:
dart run flutter_artifact_lint ios
The CLI auto-detects Flutter's default iOS outputs:
build/ios/iphoneos/*.appbuild/ios/archive/*.xcarchive
You can also pass the artifact explicitly:
dart run flutter_artifact_lint ios build/ios/iphoneos/Runner.app
Interface
Usage:
flutter_artifact_lint ios [artifact] [options]
Arguments:
artifact Path to .app or .xcarchive.
If omitted, Flutter default build outputs are auto-detected.
Options:
--format <text|json> Output format (default: text)
--fail-on <failed|warned|none> Exit policy (default: failed)
--output <file> Write report to file
--baseline <file> Suppress known findings from YAML baseline
--verbose Show evidence, rule ids, and finding paths
-h, --help Show help
Exit codes:
0: allowed to continue under the selected--fail-onpolicy1: blocked by findings under the selected policy2: input, configuration, or tool error
Rule surface
The iOS scanner combines bundle metadata, Info.plist, privacy manifest files,
plain binary strings, and structured Mach-O metadata. It is designed to run
before App Store upload so CI can stop obvious release problems early while
still marking evidence-only risks as warnings.
failed findings are deterministic release-artifact problems: missing or invalid
Info.plist, placeholder purpose strings, missing export compliance, missing
launch screen or orientations, global ATS arbitrary loads, inconsistent always
location background mode, and malformed privacy manifest declarations.
warned findings are binary evidence that needs developer confirmation:
permission APIs without matching purpose strings, notification SDK/API evidence,
required reason API traces without matching PrivacyInfo.xcprivacy categories,
UIWebView, private selector strings, private framework links, simulator
Mach-O slices, and dynamic code execution traces.
JSON output and verbose text output include evidence source paths when binary
tokens can be traced back to files in the artifact.
info findings describe the scanned artifact, bundle identity, version,
Mach-O architecture, build, load-command, symbol, Objective-C, Swift, and
linkedit metadata, and whether signing data is available.
App extensions under PlugIns/*.appex are scanned as their own bundles for
permission-purpose checks, so extension evidence is evaluated against the
extension Info.plist instead of the main app Info.plist.
Unsigned Flutter .app outputs cannot prove final codesign, provisioning
profile, push entitlement, app group, iCloud, keychain access group, or
associated-domain state. Run this tool before signing to catch binary and bundle
issues early, then add a signed-artifact check before release if those
entitlements matter.
All emitted rule IDs are documented in doc/rules.md.
Required-reason API reason-code validation is based on Apple's
NSPrivacyAccessedAPIType documentation checked on 2026-05-07.
Baseline
Use a baseline to suppress reviewed findings while keeping new findings visible:
ignore:
- ruleId: ios.notification.evidence
- ruleId: ios.permission.camera.missing
path: Frameworks/CameraSDK.framework/CameraSDK
Baseline entries with unknown rule IDs fail the CLI. Entries that do not match
any current finding are reported as baseline.unused so stale suppressions can
be removed.
Then run:
dart run flutter_artifact_lint ios --baseline flutter_artifact_lint.yml
Development
Fast tests:
dart test
Mach-O parser and string-scanner gap matrix:
dart test test/macho_test.dart test/string_scanner_gap_matrix_test.dart
Real Flutter iOS build E2E test:
dart test integration_test
The E2E test requires macOS, Flutter, and Xcode.
Publishing
Before publishing to pub.dev, run:
dart pub publish --dry-run
The package is distributed under the MIT License. Add repository or
homepage metadata in pubspec.yaml once the public repository URL is final.