From 25d9c3a6746901df0ef7e4b83da458eb071e3bfd Mon Sep 17 00:00:00 2001 From: Daniel Vogelheim Date: Thu, 4 May 2023 18:15:29 +0200 Subject: [PATCH 1/3] First draft: explainer-new. --- explainer-new.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 explainer-new.md diff --git a/explainer-new.md b/explainer-new.md new file mode 100644 index 0000000..85b8d1c --- /dev/null +++ b/explainer-new.md @@ -0,0 +1,72 @@ +# Sanitizer API: New API Explainer + +Context: Various disagreements over API details lead to a re-design of the +Sanitizer API. The current (April '23) spec draft no longer represents a +consensus within the Sanitizer working group (or other interested groups, like +the HTML WG). This explainer sketches out a new API design: + +## A new proposal, based on #192: + +There is a 2x2 set of methods that parse and filter the resulting node tree: +On one axis they differ in the parsing context and are analogous to `innerHTML` +and `DOMParser.parseFromString`, respectively; on the other, they differ in +whether they enforce an XSS-focused security baseline or not. These two aspects +pair well and yield: + +- `Element.setHTML(string, {options})` - Parses `string` using `this` as + context element, like assigning to `innerHTML` would ; applies a filter, + while enforcing an XSS-focused baseline; and finally replaces the children + of `this` with the results. +- `Element.setHTMLUnsafe(string, {options})` - Like above, but it does not + enforce a baseline. E.g. if the filter is configured to allow a `Hello World!"); - element.setHTML("Hello World!"); -``` - -Oftentimes, applications have additional — often stricter — -requirements beyond just script execution. For example, in a certain context -an application might want to allow formatted text, but no structural or other -complex markup. To accommodate this, the API allows for creation of multiple -`Sanitizer` instances, which can be customized on creation. - -Example: -```js - // The generalized form of the `.setHTML` call takes an options bag with a - // `sanitizer` value: - const sanitizer = new Sanitizer(); - element.setHTML(untrusted_input, {sanitizer: sanitizer}); - - // We must sanitize untrusted inputs, but we may want to restrict it further - // to meet other, related design goals. Here, we'll have a Sanitizer that - // allows for character-level formatting elements, plus the class= attribute - // on any element, but nothing else. - const for_display = new Sanitizer({ - allowElements: ['span', 'em', 'strong', 'b', 'i'], - allowAttributes: {'class': ['*']} - }); - - const untrusted_example = "Well, hello there!" - - // Well, hello there! - element.setHTML(untrusted_example, {sanitizer: sanitizer}); - element.setHTML(untrusted_example); // Same, since it uses a default instance. - - // Well, hello there! - element.setHTML(untrusted_example, {sanitizer: for_display}); -``` - -It is the overarching design goal of the Sanitizer API to be safe and simple, -at the same time. Therefore the API is not only safe by default, but is also -perma-safe. The Sanitizer will enforce a baseline that does not allow script -execution, even if a developer may have inadvertently configured script-ish -elements or attributes to be supported. - -Example: -```js - const misconfigured = new Sanitizer({ - allowElement: ["s", "strike", "span", "script"], - allowAttributes: {"class": ["*"], "style": ["span"], "onclick": ["*"]} - }); - - const untrusted_input = "something"; - // something - element.setHTML(untrusted_input, {sanitizer: misconfigured}); - - // Sanitizer will refuse to insert script content, even if you (inadvertently) - // call it on an inappropriate context element, like