If you’re a CodePen user, this shouldn’t affect you aside from potentially seeing some console noise while we work this out. Carry on!

At CodePen we have Embedded Pens which are shown in an <iframe>. These contain user-authored code served from a non-same-origin URL. We like to be both safe and as permissive as possiblewith what we allow users to build and test.

The sandbox attribute helps us with safety and while there are some issues with it that we’ll get to later, this is mostly about the allow attribute.

Say a user wants to use the navigator.clipboard.writeText() API. So they write JavaScript like:

button.onclick = async () => { try { await navigator.clipboard.writeText(`some text`); console.log('Content copied to clipboard'); } catch (err) { console.error('Failed to copy: ', err); }} The Embedded Pen is placed on arbitrary origins, for example: chriscoyier.net. The src of the <iframe> is at codepen.io, so there is an origin mismatch there. Since the JavaScript in the iframe is not same-origin, it is subject to permissions policies.

Without the allow attribute on our <iframe> it would throw an error when the user tries to execute that JavaScript.

Failed to copy: NotAllowedError: Failed to execute 'writeText' on 'Clipboard': The Clipboard API has been blocked because of a permissions policy applied to the current document. See https://crbug.com/414348233 for more details. This is an easy fix. We make sure that allow attribute is on the <iframe>, like this, targeting the exact feature we want to allow at any origin:

```

``` But here’s where the problem comes in…

The (new) Nested Iframe IssueCodePen’s Embedded Pens have a nested <iframe> structure like this:

The code essentially looks like this, illustrated with the iframe page content inline.

```

`` To support the JavaScript APIs, we need to put theallow` attribute on the Preview iframe like this:

```

``` Uh-oh! Now we have a problem.

Recently (Chrome 136+) this error gets thrown as soon as the nested iframe has the allow attribute:

[Violation] Potential permissions policy violation: clipboard-write is not allowed in this document. With our complete list (which I’ll include below), these error logs are very intense and noisy:

Can’t we just put the allow attributes on both <iframe>s? Yes and no.

Now we run into a second problem that we’ve been working around for many years.

Every browser has a different set of allow attribute values that it supports. If you use a value that isn’t supported, it throws console errors or warnings about those attributes. This is noisy or scary to users who might think it’s their own code causing the issue, and it’s entirely outside of their control.

The list of allow values for Google ChromeWe need all of these values in the allow attribute for users to test safe browser APIs. We constantly adjust to add new APIs, often that our users ask for directly.

```

``` There are even some quite-new AI-related attributes reflecting brand new browser APIs.

Example of allow value errorsIf were to ship those allow attribute values on all <iframe>s that we generate for Embedded Pens, here’s what it would look like in Firefox:

At the moment, Firefox actually displays three sets of these warnings. That’s a lot of console noise.Safari, at the moment, isn’t displaying errors or warnings about unsupported allow attribute values, but I believe they have in the past.

Chrome itself throws warnings. If I include an unknown policy like fartsandwich, it will throw a warning like:

Unrecognized feature: 'fartsandwich'. Those AI-related attributes require a trial which also throw warnings, so most users get that noise as well.

We Need To Do User-Agent Sniffing (sorry!)To avoid all this noise and stop scaring users, we detect the user-agent client-side and generate the iframe attributes based on what browser we’re pretty sure it is.

Here’s our current data and choices for the allow attribute: export default { allowAttributes: { chrome: [ 'accelerometer', 'bluetooth', 'camera', 'clipboard-read', 'clipboard-write', 'display-capture', 'encrypted-media', 'geolocation', 'gyroscope', 'language-detector', 'language-model', 'microphone', 'midi', 'rewriter', 'serial', 'summarizer', 'translator', 'web-share', 'writer', 'xr-spatial-tracking' ], firefox: [ 'camera', 'display-capture', 'geolocation', 'microphone', 'web-share' ], default: [ 'accelerometer', 'ambient-light-sensor', 'camera', 'display-capture', 'encrypted-media', 'geolocation', 'gyroscope', 'microphone', 'midi', 'payment', 'serial', 'vr', 'web-share', 'xr-spatial-tracking' ] }}; We’ve been around long enough to know that user-agent sniffing is rife with problems. We’ve also been around long enough that you gotta do what you gotta do to solve problems. While we don’t love it, we’ve been doing this for many years and it’s mostly worked.

Can’t Sniff Without ‘Script.Unfortunately the user-agent sniffing is limited to where a script can run on the parent page which has the <iframe>:

```

`` CodePen has a couple of features where the