Mobile Permission and Privacy Manifest Review
Permissions, entitlements, and privacy declarations are executable claims. They say which device capability an app can request, which signed capability it has, and which collection or API use is declared. Review them together. A clean runtime prompt does not fix an overbroad Android manifest; a correct manifest does not fix an iOS privacy manifest that omits SDK collection.
OWASP MASVS-PRIVACY treats inadequate permission management and inadequate collection declarations as security issues. Start with feature purpose, not available APIs. If no current feature needs a capability, omit it from build inputs.
Make purpose mapping release evidence
Build a small capability register before release. Each row needs one accountable owner and a testable result.
| Capability | User action and purpose | Platform declaration | Owner | Pass / fail |
|---|---|---|---|---|
| Camera | user taps “scan document” | android.permission.CAMERA; iOS camera usage text | scan owner | pass: prompt follows tap; fail: requested at launch |
| Approximate location | user taps nearby results | Android location permission; iOS location usage text | discovery owner | pass: coarse option works; fail: precise location required without need |
| Notifications | user enables alerts | Android runtime notification permission where applicable; APNs entitlement | messaging owner | pass: app works after denial; fail: prompt blocks first use |
| Contacts | user chooses import | platform declaration only in builds with import | identity owner | pass: no request until import; fail: SDK adds it silently |
Map data flow too: source, on-device use, server recipient, retention owner, and public disclosure. “SDK needs it” is evidence to inspect, not permission approval.
Android: minimize declaration and request at runtime
Android distinguishes normal and dangerous permissions; dangerous permissions need runtime handling on supported Android versions. Declaration in AndroidManifest.xml does not grant permission. Request only when user starts feature, explain the benefit in product UI if context needs it, and make denial a supported state.
<!-- AndroidManifest.xml: camera exists only for document scanning -->
<uses-permission android:name="android.permission.CAMERA" />
if (hasCameraPermission()) openScanner()
else requestCameraPermission() // called from scan button, not app startup
Keep variants honest. A flavor without scanning should not inherit camera permission through merged manifests. Inspect merged manifest and final APK/AAB, not only source manifest. Remove permissions introduced by libraries when unused, or replace/configure library after confirming feature impact. Do not request background location, broad storage, contacts, microphone, or accessibility capability because it might help later.
Test grant, deny, deny permanently, revoke in system settings, partial media selection where relevant, process restart, and upgrade path. Pass when feature degrades clearly and safely. Fail when denial crashes, loops prompts, blocks unrelated UI, or leaves data collection running.
Apple: usage strings, entitlements, and privacy manifest
iOS usage descriptions tell user why protected data is requested; wording must match actual feature. Entitlements are signed capabilities and require separate review. Remove unused app groups, associated domains, background modes, push, Keychain sharing, or other capability entries rather than relying on runtime code not to call them.
Apple documents PrivacyInfo.xcprivacy as a property-list file for data collected, tracking domains when tracking applies, and required-reason APIs. Include it in target resources. Review app and third-party SDK manifests; reconcile result with code, network behavior, and App Store privacy disclosures. Do not invent required-reason codes: select only Apple-documented reason values applicable to actual use.
<!-- PrivacyInfo.xcprivacy: minimal illustrative root keys -->
<dict>
<key>NSPrivacyTracking</key><false/>
<key>NSPrivacyCollectedDataTypes</key><array/>
<key>NSPrivacyAccessedAPITypes</key><array/>
</dict>
Empty arrays are correct only if audit confirms no collected-data or required-reason entries. This snippet is structure, not universal content. Add required declarations from actual app and SDK behavior, then confirm file belongs to release target.
Gate, exceptions, remediation
Owner: feature owner writes purpose; Android/iOS owners prove package configuration; privacy owner reconciles data declarations; release owner accepts evidence. Evidence: purpose register, merged Android manifest, signed iOS entitlements, PrivacyInfo.xcprivacy, SDK inventory, runtime captures using synthetic data, and denial-path test results.
Pass when every declaration maps to current feature, request timing explains purpose, least-privilege option works, denial is safe, and iOS declarations match build evidence. Fail when unused permission/entitlement remains, SDK behavior lacks owner, privacy manifest is absent or unbundled, or public disclosure conflicts with tested flow. Exception needs a named feature, affected build, risk owner, expiry, compensating control, and removal date. Remediate by deleting declaration, narrowing scope or precision, moving request to user action, fixing denial handling, updating manifest/disclosure, then rebuilding and retesting final artifacts.
Mobile application penetration testing can compare shipped packages with these release records.
Worked case: denied location permission
Install fresh build, deny precise and approximate location permission, then exercise every screen that offers nearby results. Expected output is usable screen with explicit explanation and no background request loop, crash, or silent fallback to previously cached coordinates. Inspect network trace for location fields before and after denial; request must omit unavailable value rather than substitute a fabricated default. Grant approximate permission, repeat, and confirm UI does not describe result as precise. Closure evidence pairs permission-state screenshots with request schema captures and product decision for degraded behavior.
Edge tests before closure
Revoke permission during foreground use, upgrade from prior app version, and choose “don’t ask again” where platform offers it. Check runtime rationale points to current feature, not generic collection language. Run same flow through Indonesian copy; native explanation must preserve choice and consequence. Close after test owner records build hash, device OS, permission state, observed output, and remediation owner for any divergent screen.