iOS 14 introduced photo library limited access: users can grant access to a specific selection of photos rather than their entire library. An app that requests full photo library access for a feature that only needs one photo — such as a profile picture upload — forces users to choose between granting access to their entire camera roll or denying the feature entirely. This is a GDPR Art. 5(1)(c) data-minimisation violation. Apple's App Store reviewers check whether apps use the full library permission when limited access would suffice; requesting more than necessary is grounds for a guideline violation. CWE-272 (Least Privilege Violation) applies when an app acquires more access than its operation requires.
Low because the over-permission is scoped to photo access rather than more sensitive data types, but it remains a GDPR Art. 5(1)(c) data-minimisation violation and a common App Store Review guideline failure.
Use expo-image-picker with default settings for features that need one or a few photos — it automatically uses the limited access picker on iOS 14+. Only declare NSPhotoLibraryUsageDescription for features that browse the full library.
import * as ImagePicker from 'expo-image-picker'
// Profile photo upload: limited access is appropriate
async function pickProfilePhoto() {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
// On iOS 14+, this presents the limited-access picker by default
})
if (!result.canceled) {
uploadProfilePhoto(result.assets[0].uri)
}
}
In app.json, only add NSPhotoLibraryUsageDescription if a feature genuinely browses the full library. Use NSPhotoLibraryAddOnlyUsageDescription for save-only operations (e.g., saving generated images). Remove NSPhotoLibraryUsageDescription if all photo access goes through the picker.
ID: mobile-permissions-privacy.data-handling.photo-library-limited
Severity: low
What to look for: Count all photo library access calls (ImagePicker, photo library APIs). For each, check whether the limited permission scope is used on iOS 14+. Check Info.plist for NSPhotoLibraryUsageDescription vs NSPhotoLibraryAddOnlyUsageDescription entries.
Pass criteria: If app accesses photo library, at least 100% of photo access calls use the limited permission (allowing users to select photos) when the feature only needs a single photo or a subset. Only full access is requested if the feature genuinely requires browsing all photos.
Fail criteria: App requests full photo library access for features that could work with limited access (e.g., profile photo upload). OR no photo library permission granularity is implemented on iOS 14+.
Skip (N/A) when: App does not access photo library (no photo picker or library import calls found), or targets pre-iOS 14 only.
Detail on fail: Quote the permission config. "App requests full photo library access but only needs to upload a single photo (should use limited access)" or "Photo library permission not differentiated for iOS 14+ (user cannot choose selected photos)"
Remediation: Use limited photo access on iOS 14+ to give users privacy control:
{
"app.json": {
"ios": {
"infoPlist": {
"NSPhotoLibraryUsageDescription": "Select photos to share",
"NSPhotoLibraryAddOnlyUsageDescription": "Save photos from the app"
}
}
}
}
import * as ImagePicker from 'expo-image-picker'
async function pickPhoto() {
// This uses limited photo access on iOS 14+
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsMultiple: false,
})
}