Sanitizing HTML
editMarkup written by users (comments, messages, rich text) must not run scripts in your pages. SetHtml parses the markup and removes everything that can run a script, with the HTML Sanitizer API of the browsers:
var document = HtmlDocumentParser.ParseDocument("<!DOCTYPE html><div id=comment></div>");
var comment = document.GetElementById("comment")!;
// SetHtml removes what can run scripts: script elements, event handlers, javascript: URLs...
comment.SetHtml("""<p onclick="steal()">Nice post! <a href="javascript:steal()">Link</a><script>steal()</script></p>""");
Assert.Equal("<p>Nice post! <a>Link</a></p>", comment.InnerHtml);
// A configuration restricts the elements and the attributes further
comment.SetHtml("<h1>Title</h1><p title=t>Some <b>bold</b> and <i>italic</i> text</p>", new SetHtmlOptions
{
Sanitizer = new SanitizerConfig { Elements = ["p", "b"], Attributes = [] },
});
Assert.Equal("<p>Some <b>bold</b> and text</p>", comment.InnerHtml);
// Parse a whole document with the same rules
var page = Document.ParseHtml("<title>Post</title><p onclick=steal()>Hello</p>");
Assert.Equal("<p>Hello</p>", page.Body!.InnerHtml);Safe and unsafe methods
| Methods | Sanitized |
|---|---|
| Element.SetHtml, Document.ParseHtml | Always: the elements and attributes that can run scripts are removed, whatever the configuration |
| BeforeHtml, AfterHtml, ReplaceWithHtml, AppendHtml... | The same, for the positional insertions |
Element.SetHtmlUnsafe, Document.ParseHtmlUnsafe and the ...HtmlUnsafe methods | Only by the configuration they are given |
| InnerHtml, OuterHtml, InsertAdjacentHtml | Never: for trusted markup only |
The default configuration is a list of allowed elements and attributes. It removes the script elements, the event handler attributes (onclick...), the javascript: URLs, the iframe elements and the comments. class, id and the data-* attributes are not in the list either: give a configuration to keep them.
Configure the sanitizer
A SanitizerConfig lists what is allowed or removed. Give it in SetHtmlOptions.Sanitizer, or create a Sanitizer from it to reuse it:
| Option | Effect |
|---|---|
| Elements, Attributes | The elements and attributes that are kept: the others are removed |
| RemoveElements, RemoveAttributes | The elements and attributes that are removed: the others are kept |
| ReplaceWithChildrenElements | The elements that are replaced by their content (keep the text of a b, without the b) |
| Comments, DataAttributes | Whether the comments and the data-* attributes are kept |
The markup is sanitized while it is parsed, as in the browsers. A removed element is never created in the page, so it runs no custom element constructor and attaches no declarative shadow root. The content of a replaced element goes where the element would have gone: with div in ReplaceWithChildrenElements, <table><div>x</div><tr><td>y</table> gives x<table><tbody><tr><td>y</td></tr></tbody></table>, because the parser moves a div out of a table.
A Sanitizer can also be changed with its methods (AllowElement, RemoveElement, AllowAttribute, SetComments...), and Get() returns its configuration. A configuration that has both Elements and RemoveElements, or that lists an element twice, is invalid: the Sanitizer constructor throws a TypeErrorException.