Accessing device hardware through undocumented native paths instead of the platform's official permission API means the app bypasses the OS-level permission gate entirely. OWASP Mobile Top 10 A01 (Improper Credential Usage) includes improper access to device hardware as a credential misuse vector. CWE-272 (Least Privilege Violation) is directly triggered when an app reads from a sensor without first verifying it has been granted access. Beyond correctness, bypassing react-native-permissions or expo-permissions means your code will behave unpredictably when users revoke permissions mid-session, because the native module has no revocation-aware wrapper — the call will silently fail or crash instead of returning a denied status.
High because bypassing the standard permission API means the app can silently access hardware after a user revokes permission mid-session, with no graceful fallback — a direct OWASP A01 and CWE-272 violation.
Replace direct native module calls with react-native-permissions (bare React Native) or expo-permissions / expo-location (Expo). Always distinguish between LOCATION_WHEN_IN_USE and LOCATION_ALWAYS.
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions'
import { Platform } from 'react-native'
async function requestLocationPermission(): Promise<boolean> {
const permission =
Platform.OS === 'ios'
? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
const result = await request(permission)
return result === RESULTS.GRANTED
}
For Expo, use the typed exports from expo-location, expo-camera, or expo-contacts — they wrap the same Keychain/Keystore-aware permission APIs and handle revocation correctly.
ID: mobile-permissions-privacy.permission-requests.sensitive-permissions-api
Severity: high
What to look for: List all sensitive permissions used (camera, location, contacts, calendar, photos, microphone, health). For each, check whether it is requested via a standard permission API (react-native-permissions, expo-permissions) rather than direct native calls. Quote the actual import and API call used.
Pass criteria: At least 100% of sensitive permissions are requested via a standard permission library or Expo API. Location permission requests specify when-in-use or always explicitly (not just a generic location request). Report the count even on pass: "All N sensitive permissions use proper API."
Fail criteria: Any sensitive permission is accessed directly without checking permission status first (e.g., calling native camera without a permission check). OR location permission does not distinguish between permission types.
Skip (N/A) when: App uses no sensitive permissions (no camera, location, contacts, calendar, photos, microphone, or health APIs).
Detail on fail: Specify which permissions bypass the API. Quote the actual code. Example: "Camera is accessed directly via NativeModules.CameraModule without permission check" or "Location permission requested but 'always' vs 'when-in-use' distinction not implemented"
Remediation: Use a standardized permission API to handle permission checks and requests. For React Native, use react-native-permissions or for Expo, use expo-permissions:
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions'
async function requestLocationPermission() {
const result = await request(
Platform.OS === 'ios'
? PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION
)
return result === RESULTS.GRANTED
}