Denied permissions do not crash the app
Why it matters
An unhandled promise rejection from a permission-gated API call causes a visible red screen in development and a silent crash in production — neither outcome is acceptable. CWE-391 (Unchecked Error Condition) and CWE-755 (Improper Handling of Exceptional Conditions) directly describe this pattern. Apps that call Geolocation.getCurrentPosition(), Contacts.getAll(), or Camera.takePicture() without a preceding permission check assume the permission is permanently granted — but users can revoke permissions mid-session in iOS and Android Settings. The revocation takes effect immediately: the next call to the native API will fail with an authorization error, and if the call is not wrapped in a try-catch, the app crashes. This is the leading cause of crash loops reported after users update their privacy settings.
Severity rationale
Medium because a crash on permission denial is reproducible by any user who revokes a permission mid-session — a common action — causing complete feature unavailability and crash reports that surface in App Store reviews.
Remediation
Wrap every protected API call in a try-catch and check permission status before the call. Never assume a previously granted permission is still in effect.
import { check, PERMISSIONS, RESULTS } from 'react-native-permissions'
import Geolocation from '@react-native-community/geolocation'
async function getCurrentLocation(): Promise<{ lat: number; lng: number } | null> {
try {
const status = await check(
Platform.OS === 'ios'
? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
)
if (status !== RESULTS.GRANTED) {
return null // Feature degrades gracefully
}
return await new Promise((resolve, reject) =>
Geolocation.getCurrentPosition(
pos => resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude }),
reject,
{ timeout: 5000 }
)
)
} catch (error) {
console.warn('Location unavailable:', error)
return null // Never throw — callers receive null and handle accordingly
}
}
Detection
-
ID:
no-crash-on-denial -
Severity:
medium -
What to look for: Enumerate all permission-gated API calls (Geolocation, Camera, Contacts, etc.). For each, check whether a try-catch or error handler wraps the call. Count how many have defensive permission checks before the API call.
-
Pass criteria: Zero permission-gated features crash when permissions are denied. At least 100% of protected API calls have try-catch or error handling that prevents crashes. App either disables features or shows clear error messages.
-
Fail criteria: Any feature crashes or becomes unresponsive when a permission is denied. Any protected API call lacks error handling.
-
Skip (N/A) when: App requests no permissions (no permission-gated API calls found).
-
Detail on fail: Quote the unprotected API call. Example:
"App crashes with unhandled promise rejection when location permission denied in location tracking screen" -
Remediation: Check permissions before accessing protected APIs:
import { check, PERMISSIONS, RESULTS } from 'react-native-permissions' async function getCurrentLocation() { try { const status = await check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE) if (status === RESULTS.GRANTED) { return Geolocation.getCurrentPosition(/* callback */) } else { throw new Error('Location permission not granted') } } catch (error) { console.warn('Location unavailable:', error.message) return null } }
External references
- cwe · CWE-391 — Unchecked Error Condition
- cwe · CWE-755 — Improper Handling of Exceptional Conditions
- iso-25010:2011 · reliability.fault-tolerance — Fault Tolerance
Taxons
History
- 2026-04-18·v1.0.0·Initial import from mobile-permissions-privacy·automated