Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions files/en-us/web/api/element/sethtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ browser-compat: api.Element.setHTML
The **`setHTML()`** method of the {{domxref("Element")}} interface provides an XSS-safe method to parse and sanitize a string of HTML and insert it into the DOM as a subtree of the element.

The method removes any elements and attributes that are considered XSS-unsafe, even if allowed by a passed sanitizer.
Notably, the following elements are always removed: {{HTMLElement("script")}}, {{HTMLElement("frame")}}, {{HTMLElement("iframe")}}, {{HTMLElement("object")}}, {{SVGElement("use")}}, event handler attributes, and data attributes.
Notably, the following elements are always removed: {{HTMLElement("script")}}, {{HTMLElement("frame")}}, {{HTMLElement("iframe")}}, {{HTMLElement("embed")}}, {{HTMLElement("object")}}, {{SVGElement("use")}}, and event handler attributes.

It is recommended (if supported) as a drop-in replacement for {{domxref("Element.innerHTML")}} when setting a user-provided string of HTML.

Expand Down Expand Up @@ -119,7 +119,7 @@ The HTML defines two {{htmlelement("button")}} elements for applying different s

```css hidden
#log {
height: 220px;
height: 320px;
overflow: scroll;
padding: 0.5rem;
border: 1px solid black;
Expand Down Expand Up @@ -148,8 +148,11 @@ We also define the handler for the reload button.
// Define unsafe string of HTML
const unsanitizedString = `
<div>
<p>This is a paragraph. <button onclick="alert('You clicked the button!')">Click me</button></p>
<script src="path/to/a/module.js" type="module"></script>
<p>Paragraph to inject into shadow DOM.
<button onclick="alert('You clicked the button!')">Click me</button>
</p>
<script src="path/to/a/module.js" type="module"><\/script>
<p data-id="123">Para with <code>data-</code> attribute</p>
</div>
`;

Expand All @@ -169,9 +172,9 @@ defaultSanitizerButton.addEventListener("click", () => {

// Log HTML before sanitization and after being injected
logElement.textContent =
"Default sanitizer: remove script element and onclick attribute\n\n";
"Default sanitizer: remove script element, onclick attribute, data- attribute\n\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${target.innerHTML}`);
log(`\n\nsanitized: ${target.innerHTML}`);
});
```

Expand All @@ -191,7 +194,7 @@ allowScriptButton.addEventListener("click", () => {
logElement.textContent =
"Sanitizer: {elements: ['div', 'p', 'script']}\n Script removed even though allowed\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${target.innerHTML}`);
log(`\n\nsanitized: ${target.innerHTML}`);
});
```

Expand All @@ -205,9 +208,11 @@ allowScriptButton.addEventListener("click", () => {
#### Results

Click the "Default" and "allowScript" buttons to see the effects of the default and custom sanitizer, respectively.
Note that in both cases the `<script>` element and `onclick` handler are removed, even if explicitly allowed by the sanitizer.

{{EmbedLiveSample("setHTML() live example","100","350px")}}
Note that because we are using a safe sanitization method, in both cases the `<script>` element and `onclick` handler are removed, even if explicitly allowed by the sanitizer.
However while the `data-` attribute is removed with the default sanitizer, it is allowed when we pass a sanitizer.

{{EmbedLiveSample("setHTML() live example","100","450px")}}

## Specifications

Expand Down
15 changes: 9 additions & 6 deletions files/en-us/web/api/element/sethtmlunsafe/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ The HTML defines two {{htmlelement("button")}} elements for calling the method w

```css hidden
#log {
height: 240px;
height: 320px;
overflow: scroll;
padding: 0.5rem;
border: 1px solid black;
Expand Down Expand Up @@ -250,8 +250,11 @@ We also define the handler for the reload button.
// Define unsafe string of HTML
const unsanitizedString = `
<div>
<p>This is a paragraph. <button onclick="alert('You clicked the button!')">Click me</button></p>
<script src="path/to/a/module.js" type="module"></script>
<p>Paragraph to inject into shadow DOM.
<button onclick="alert('You clicked the button!')">Click me</button>
</p>
<script src="path/to/a/module.js" type="module"><\/script>
<p data-id="123">Para with <code>data-</code> attribute</p>
</div>
`;

Expand All @@ -273,7 +276,7 @@ buttonNoSanitizer.addEventListener("click", () => {
logElement.textContent =
"No sanitizer: string should be injected without filtering\n\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${target.innerHTML}`);
log(`\n\nsanitized: ${target.innerHTML}`);
});
```

Expand All @@ -292,7 +295,7 @@ allowScriptButton.addEventListener("click", () => {
// Log HTML before sanitization and after being injected
logElement.textContent = "Sanitizer: {elements: ['div', 'p', 'script']}\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${target.innerHTML}`);
log(`\n\nsanitized: ${target.innerHTML}`);
});
```

Expand All @@ -311,7 +314,7 @@ When you click the "None" button, you should see that the input and output match
When you click the "allowScript" button the `<script>` element is still present, but the `<button>` element is removed.
With this approach you can create safe HTML, but you aren't forced to.

{{EmbedLiveSample("setHTMLUnsafe() live example","100","380px")}}
{{EmbedLiveSample("setHTMLUnsafe() live example","100","450px")}}

## Specifications

Expand Down
24 changes: 16 additions & 8 deletions files/en-us/web/api/shadowroot/sethtml/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ browser-compat: api.ShadowRoot.setHTML

The **`setHTML()`** method of the {{domxref("ShadowRoot")}} interface provides an XSS-safe method to parse and sanitize a string of HTML, which then replaces the existing tree in the Shadow DOM.

The method removes any elements and attributes that are considered XSS-unsafe, even if allowed by a passed sanitizer.
Notably, the following elements are always removed: {{HTMLElement("script")}}, {{HTMLElement("frame")}}, {{HTMLElement("iframe")}}, {{HTMLElement("embed")}}, {{HTMLElement("object")}}, {{SVGElement("use")}}, and event handler attributes.

It is recommended (if supported) as a drop-in replacement for {{domxref("ShadowRoot.innerHTML")}} when setting a user-provided string of HTML.

## Syntax
Expand Down Expand Up @@ -134,7 +137,7 @@ The HTML defines two {{htmlelement("button")}} elements for applying different s

```css hidden
#log {
height: 220px;
height: 320px;
overflow: scroll;
padding: 0.5rem;
border: 1px solid black;
Expand Down Expand Up @@ -170,8 +173,11 @@ We also get variable `shadow`, which is our handle to the shadow root.
// Define unsafe string of HTML
const unsanitizedString = `
<div>
<p>Paragraph to inject into shadow DOM. <button onclick="alert('You clicked the button!')">Click me</button></p>
<script src="path/to/a/module.js" type="module"></script>
<p>Paragraph to inject into shadow DOM.
<button onclick="alert('You clicked the button!')">Click me</button>
</p>
<script src="path/to/a/module.js" type="module"><\/script>
<p data-id="123">Para with <code>data-</code> attribute</p>
</div>
`;

Expand All @@ -190,9 +196,9 @@ defaultSanitizerButton.addEventListener("click", () => {

// Log HTML before sanitization and after being injected
logElement.textContent =
"Default sanitizer: remove &lt;script&gt; element and onclick attribute\n\n";
"Default sanitizer: remove script element, onclick attribute, data- attribute\n\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${shadow.innerHTML}`);
log(`\n\nsanitized: ${shadow.innerHTML}`);
});
```

Expand All @@ -212,7 +218,7 @@ allowScriptButton.addEventListener("click", () => {
logElement.textContent =
"Sanitizer: {elements: ['div', 'p', 'script']}\n Script removed even though allowed\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${shadow.innerHTML}`);
log(`\n\nsanitized: ${shadow.innerHTML}`);
});
```

Expand All @@ -226,9 +232,11 @@ allowScriptButton.addEventListener("click", () => {
#### Results

Click the "Default" and "allowScript" buttons to see the effects of the default and custom sanitizer, respectively.
Note that because we are using a same sanitization method, in both cases the `<script>` element and `onclick` handler are removed, even if explicitly allowed by the sanitizer.

{{EmbedLiveSample("setHTML() live example","100","350px")}}
Note that because we are using a safe sanitization method, in both cases the `<script>` element and `onclick` handler are removed, even if explicitly allowed by the sanitizer.
However while the `data-` attribute is removed with the default sanitizer, it is allowed when we pass a sanitizer.

{{EmbedLiveSample("setHTML() live example","100","450px")}}

## Specifications

Expand Down
15 changes: 9 additions & 6 deletions files/en-us/web/api/shadowroot/sethtmlunsafe/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ The HTML defines two {{htmlelement("button")}} elements for injecting the HTML w

```css hidden
#log {
height: 250px;
height: 320px;
overflow: scroll;
padding: 0.5rem;
border: 1px solid black;
Expand Down Expand Up @@ -269,8 +269,11 @@ We also get variable `shadow`, which is our handle to the shadow root.
// Define unsafe string of HTML
const unsanitizedString = `
<div>
<p>Paragraph to inject into shadow DOM. <button onclick="alert('You clicked the button!')">Click me</button></p>
<script src="path/to/a/module.js" type="module"></script>
<p>Paragraph to inject into shadow DOM.
<button onclick="alert('You clicked the button!')">Click me</button>
</p>
<script src="path/to/a/module.js" type="module"><\/script>
<p data-id="123">Para with <code>data-</code> attribute</p>
</div>
`;

Expand All @@ -289,7 +292,7 @@ buttonNoSanitizer.addEventListener("click", () => {
// Log HTML before sanitization and after being injected
logElement.textContent = "No sanitizer\n\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${shadow.innerHTML}`);
log(`\n\nsanitized: ${shadow.innerHTML}`);
});
```

Expand All @@ -306,7 +309,7 @@ allowScriptButton.addEventListener("click", () => {

// Log HTML before sanitization and after being injected
logElement.textContent = "Sanitizer: {elements: ['div', 'p', 'script']}\n";
log(`\nunsanitized: ${unsanitizedString}`);
log(`\n\nunsanitized: ${unsanitizedString}`);
log(`\nsanitized: ${shadow.innerHTML}`);
});
```
Expand All @@ -326,7 +329,7 @@ When you click the "None" button, you should see that the input and output match
When you click the "allowScript" button the `<script>` element is still present, but the `<button>` element is removed.
With this approach you can create safe HTML, but you aren't forced to.

{{EmbedLiveSample("setHTMLUnsafe() live example","100","350px")}}
{{EmbedLiveSample("setHTMLUnsafe() live example","100","450px")}}

## Specifications

Expand Down