Why the error alert still displayed event the HTML elements are sanitized?

239 Views Asked by At

The below are my use case scenario.

  1. Collect the user input.(the input must be HTML sting element)
  2. Form the HTML element with some style.
  3. After creating the HTML element, need to sanitize it and append it into the target DOM element.
  4. The DOM element rendered the sanitized element. but the error alert stil displayed.

The user input is

<img src="http://url.to.file.which/not.exist" onerror="alert(document.cookie);">

This always shows the alert.

Can someone help me to resolve this?

function createEle() {
  const wrapElement = document.createElement('div');
  wrapElement.innerHTML = document.getElementById("html-input").value;
  const html = showSanitizedHTML(wrapElement);
  console.log(html.outerHTML)
  document.getElementById("sanitized-html").innerHTML = html.innerHTML;
}

function showSanitizedHTML(value) {
  const sanitizedHTML = DOMPurify.sanitize(value.outerHTML);
  const tempWrapElement = document.createElement('div');
  tempWrapElement.innerHTML = sanitizedHTML;
  return tempWrapElement.firstElementChild;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>

3

There are 3 best solutions below

0
Quentin On

This is the sequence of events:

  1. You read the raw user input
  2. You inject the raw user input into the DOM (wrapElement.innerHTML =)
  3. You read the normalised user input back from the DOM
  4. You pass the normalised user input though DOMPurify.sanitize
  5. You inject the sanitized user input into the DOM

The alert fires due to step 2.

0
mplungjan On

The script content executes on this statement

wrapElement.innerHTML = document.getElementById("html-input").value;

So here is how to fix it

function createEle() {
  const wrapElement = document.createElement('div');
  wrapElement.innerHTML =  DOMPurify.sanitize(document.getElementById("html-input").value);
  console.log(wrapElement.outerHTML)
  document.getElementById("sanitized-html").innerHTML = wrapElement.innerHTML;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>

or shorter, assuming you want to keep using the wrapper:

document.getElementById("sanitized-html").innerHTML = wrapElement.innerHTML;
0
T.J. Crowder On

Because you're using unsanitized HTML here:

const wrapElement = document.createElement('div');
wrapElement.innerHTML = document.getElementById("html-input").value;

Don't jump through those hoops, just pass the text directly to DOMPurify:

function createEle() {
    const element = showSanitizedHTML(document.getElementById("html-input").value);
    // Your `showSanitizedHTML` is returning an **element**, not HTML,
    // so you wouldn't use `innerHTML` to put it in the DOM.
    const output = document.getElementById("sanitized-html");
    output.innerHTML = "";
    output.appendChild(element);
}

function showSanitizedHTML(html) {
    const sanitizedHTML = DOMPurify.sanitize(html);
    // Not sure what the purpose of this wrapper element is, but since
    // it only uses sanitized HTML, I left it in place.
    const tempWrapElement = document.createElement("div");
    tempWrapElement.innerHTML = sanitizedHTML;
    return tempWrapElement.firstElementChild;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>

Or more simply:

function createEle() {
    const html = showSanitizedHTML(document.getElementById("html-input").value);
    document.getElementById("sanitized-html").innerHTML = html;
}

function showSanitizedHTML(html) {
    const sanitizedHTML = DOMPurify.sanitize(html);
    return sanitizedHTML;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>

Or even more simply:

function createEle() {
    document.getElementById("sanitized-html").innerHTML = DOMPurify.sanitize(
        document.getElementById("html-input").value
    );
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
<textarea id="html-input"></textarea>
<button onclick="createEle()">Show Sanitized HTML</button>
<div id="sanitized-html"></div>