Security
What Signy does with untrusted data, and what is left to you.
Text is text
A value in an html template is set as text, never parsed as
markup. A comment that contains <img onerror=...>
shows those characters and runs nothing.
html`<p>${comment.body}</p>`; // safe, whatever the body holds
html`<p title=${comment.author}>...</p>`; // safe, set as an attribute value
Attributes that could run code
Bindings, spreads, the DOM helpers and router links refuse
three kinds of value. The attribute is left out, and dev builds log
view:unsafe-attribute when
warnings are on.
- An
onattribute given a string, such asonclick=${'alert(1)'}. Handlers must be functions. srcdoc, which holds a whole document.- A
javascript:orvbscript:URL, or adata:URL that is not an image, inhref,src,action,formaction,poster,dataorxlink:href. Spaces and control characters hidden in the scheme do not get past the check.
Every other URL is allowed, including links to other sites. Check the host yourself when a link must stay on your site.
Raw HTML
Templates have no way to insert an HTML string. When you need one, such as rendered Markdown, you write it yourself through a ref, and that is where untrusted data must be cleaned.
html`<div ref=${(el) => (el.innerHTML = sanitizeHtml(post.body, ['p', 'b', 'i', 'ul', 'li']))}></div>`;
escapeHtml(value)escapes&,<,>and both quotes.sanitizeHtml(html, tags)keeps only the listed tags, drops every attribute, and dropsscriptandstylewith their contents. Without a list it escapes everything.stripHtml(html)returns the visible text.
sanitizeHtml() removes all attributes, so links lose their
href. For rich text with links and images, use a dedicated
sanitizer such as DOMPurify. DOM.createTemplate(html)
parses an HTML string too: pass it your own markup only.
Store and query strings
store.merge()andupdate()skip a__proto__key in the data, so merging parsed JSON cannot changeObject.prototype.- A
set()path through__proto__,constructororprototype, and an assignment tostate.__proto__, throwreactivity:store-unsafe-key. - The router parses
?__proto__=xor?toString=1into plain keys of the query object, without touching prototypes.
Your part
- Never pass user data to
innerHTML,outerHTML,insertAdjacentHTML()orDOM.createTemplate()without a sanitizer. - Check URLs from users before you put them in
href, beyond the scheme check above. - Use a Content Security Policy. Signy and the
htmltemplate never calleval()ornew Function(), so'unsafe-eval'is not needed.