Permissions API
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7). No automated linting or static analysis tool in detection_hints.tools catches missing permission checks. The code_pattern hint (permissions.query) requires manual inspection or runtime testing to identify when permission state isn't checked before API calls.
Closest to 'simple parameterised fix' (e3). The quick_fix describes a straightforward pattern: add permissions.query() before the feature, branch on state (granted/prompt/denied), and add onchange listener. This refactor is localized to the component using the sensitive API and doesn't require cross-cutting changes.
Closest to 'localised tax' (b3). The choice to properly check permissions applies only to components that use sensitive APIs (geolocation, camera, microphone, etc.). Once a component implements the pattern, the tax is paid locally; other components unaffected. applies_to scope is 'web' contexts only, limiting reach.
Closest to 'serious trap' (t7). The canonical misconception directly contradicts intuition: query() does NOT request permissions — it only checks state. Developers unfamiliar with the distinction will assume query() triggers the browser prompt, when the actual request happens only on the underlying API call. This is a documented gotcha (mentioned in misconception) that contradicts the obvious naming expectation.
TL;DR
Explanation
navigator.permissions.query({ name: 'geolocation' | 'camera' | 'clipboard-read' | 'notifications' | ... }) returns a PermissionStatus with state: 'granted', 'denied', or 'prompt'. Subscribe to changes with status.onchange. Use cases: show appropriate UI (enable location button vs 'go to settings' message), avoid triggering permission prompts at bad times. Supported permissions vary by browser. Not all APIs have a corresponding permission entry — check MDN for coverage. Chrome, Firefox, and Safari have different support levels.
Common Misconception
Why It Matters
Common Mistakes
- Not checking permission before showing a feature that requires it.
- Assuming query() is available for all permissions — coverage varies by browser.
- Not reacting to permission state changes (onchange event).
Code Examples
// Immediately request — no context for user:
navigator.geolocation.getCurrentPosition(success, error);
const status = await navigator.permissions.query({ name: 'geolocation' });
if (status.state === 'granted') {
navigator.geolocation.getCurrentPosition(success);
} else if (status.state === 'prompt') {
showLocationRequestButton(); // User hasn't decided
} else {
showEnableInSettingsMessage(); // Denied
}
status.onchange = () => updateUI(status.state);