# Legacy Feature-Policy header also set

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

## Why it matters

Safari does not recognize the `Permissions-Policy` header name — it only supports the older `Feature-Policy` header. Sites that configure `Permissions-Policy` without a matching `Feature-Policy` leave Safari users with no API restrictions at all: camera, microphone, geolocation, and payment APIs remain unrestricted for embedded content. CWE-693 applies when a protection mechanism is deployed in a way that bypasses it for a significant user segment. OWASP A05 flags browser-specific security header gaps as misconfiguration — partial cross-browser coverage is operationally equivalent to no coverage for affected users.

## Severity rationale

Low because Safari's market share is significant (especially on mobile), and omitting `Feature-Policy` leaves that entire segment without any API permissions restrictions despite `Permissions-Policy` being configured.

## Remediation

Set both `Permissions-Policy` (Chromium/Firefox) and `Feature-Policy` (Safari) with matching restrictions. The syntax differs: `Feature-Policy` uses `'none'` instead of `()` and semicolons instead of commas.

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

Skip this step only if you have confirmed your deployment target is Chromium-only (Chrome extension, Electron app, kiosk). For public web apps, both headers are required.

## Detection

- **ID:** `feature-policy-compat`
- **Severity:** `low`
- **What to look for:** Check for both `Permissions-Policy` (modern) and `Feature-Policy` (legacy) headers. Safari does not support the newer `Permissions-Policy` name. Both headers should be present with matching restrictions for cross-browser coverage.
- **Pass criteria:** Both `Permissions-Policy` and `Feature-Policy` headers are present with matching restrictions — at least 3 APIs restricted in each. Count the number of restricted APIs in each header and verify they match.
- **Fail criteria:** Only `Permissions-Policy` is set without a corresponding `Feature-Policy` header, or vice versa.
- **Skip (N/A) when:** No `Permissions-Policy` header configured. Also skip if the project explicitly targets Chromium-only (e.g., Chrome extension, Electron app).
- **Detail on fail:** `"Permissions-Policy header set but no legacy Feature-Policy — Safari users have no API restrictions"` or `"Feature-Policy present but missing modern Permissions-Policy header"`
- **Remediation:** Set both headers for cross-browser compatibility. The syntax differs slightly:

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

  `Feature-Policy` uses `'none'` instead of `()` and semicolons instead of commas. This is a low-priority compatibility improvement.

## External references

- cwe CWE-693
- owasp A05

Taxons: access-control

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