# Permissions-Policy header restricts sensitive APIs

- **Pattern:** `ab-000010` (`security-headers.headers.permissions-policy`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-17
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000010
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000010)

## Why it matters

Without `Permissions-Policy`, any JavaScript executing on your page — including third-party analytics scripts, embedded widgets, and compromised CDN assets — can request access to the user's camera, microphone, and geolocation without additional browser prompts. If an XSS payload fires or a third-party script is compromised, it can silently activate hardware sensors. CWE-732 (Incorrect Permission Assignment) and OWASP A05 (Security Misconfiguration) both apply to unrestricted browser API exposure. Restricting permissions you don't use follows the principle of least privilege: a payment app has no legitimate reason to access the gyroscope.

## Severity rationale

Low because exploiting unrestricted permissions requires additional code execution context, but the policy is trivial to add and eliminates unnecessary browser API surface.

## Remediation

Add a `Permissions-Policy` header that explicitly denies browser APIs your app doesn't use. Deny everything you don't need — you can always add specific origins later.

```js
// next.config.js
headers: [{
  key: 'Permissions-Policy',
  value: 'camera=(), microphone=(), geolocation=(), payment=(), usb=()'
}]
```

The `()` syntax denies the feature for all origins, including your own pages. Use `(self)` to allow the feature only for your own origin. If you do need camera access (e.g., for a video feature), set `camera=(self)` instead of allowing it globally.

## Detection

- **ID:** `permissions-policy`
- **Severity:** `low`
- **What to look for:** Count all header configuration locations (framework config, middleware, deployment config) and check for a `Permissions-Policy` (or older `Feature-Policy`) header. Enumerate which sensitive browser APIs are restricted: camera, microphone, geolocation, etc.
- **Pass criteria:** A `Permissions-Policy` (or legacy `Feature-Policy`) header is present that restricts at least 3 of the following sensitive APIs: `camera`, `microphone`, `geolocation`, `payment`, `usb`, `magnetometer`, `gyroscope`, `accelerometer`. Each restricted API must be set to `()` (deny all) or `(self)` (same-origin only), not left unrestricted.
- **Fail criteria:** No `Permissions-Policy` header configured, or header is present but restricts fewer than 3 sensitive APIs.
- **Skip (N/A) when:** Never.
- **Detail on fail:** `"No Permissions-Policy header configured — all browser APIs available to embedded content by default"`
- **Remediation:** Permissions-Policy lets you control which browser features your site and embedded content can use:

  ```js
  headers: [{
    key: 'Permissions-Policy',
    value: 'camera=(), microphone=(), geolocation=()'
  }]
  ```

  The `()` syntax means "disallow for all origins". Add specific origins if your app needs these features: `camera=(self)`.

## External references

- cwe CWE-732
- owasp A05

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-000010
