Below are all the builders we currently provide.
Note: this type aliases the TrustedHTML Trusted Type.
Escaping all HTML entities will make sure that the result is always interpreted as text when used in an HTML context.
import {htmlEscape} from 'safevalues';
const html = htmlEscape('<img src=a >');
// SafeHtml{'<img src=a '}
An HTML sanitizer can take a user-controlled value and sanitize it to produce a SafeHtml instance.
import {sanitizeHtml} from 'safevalues';
const html = sanitizeHtml('<article>my post <script>alert(0)</script></article>');
// SafeHtml{'<article>my post</article>}
For more complex HTML constructions, use a dedicated HTML templating system
compatible with safevalues
like Lit.
Note: this type aliases the TrustedScript Trusted Type.
There can be a need to defer the evaluation of a piece of JavaScript. By preventing any interpolation in the script's value we ensure it can never contain user data.
import {safeScript} from 'safevalues';
const script = safeScript`return this;`;
// SafeScript{'return this;'}
Note: this type aliases the TrustedScriptURL Trusted Type.
Script URLs are potentially very dangerous as they allow to execute code in the current origin. Only knowing the origin from which a url is from is not sufficient to ensure its safety as many domains have certain paths that implement open-redirects to arbitrary URLs.
To ensure the safety of script URLs, we ensure that the developer knows the full
origin (either by fully specifying it or by using the current origin implicitly
with a path absolute url) as well as the path (no relative URLs are allowed &
all interpolations are passed to encodeURIComponent
)
import {trustedResourceUrl} from 'safevalues';
const url1 = trustedResourceUrl`/static/js/main.js`;
// TrustedResourceURL{'/static/js/main.js'}
const env = 'a/b';
const opt = 'min&test=1';
const url2 = trustedResourceUrl`/static/${env}/js/main.js?opt=${opt}`;
// TrustedResourceURL{'/static/a%2Fb/js/main.js?opt=min%26test%3D1'}
Note: This type doesn't wrap a Trusted Type.
import {safeStyleSheet, concatStyleSheets} from 'safevalues';
const styleSheet1 = safeStyleSheet`a { color: navy; }`;
// SafeStyleSheet{'a {color: navy;}'}
const styleSheet2 = safeStyle`b { color: red; }`;
concatStyles([styleSheet1, styleSheet2]);
// SafeStyleSheet{'a {color: navy;}b { color: red; }'}
When Trusted Types are not available, the library will automatically return simple objects that behave identically to Trusted Types, that is they don't inherit string functions, and only stringify to their inner value.
While this doesn't give as strong assurance in browsers that do not support Trusted Types, it allows you to preserve the same functional behaviour and abstract away the fact that some browser might not support Trusted Types.
To ensure that the values we produce are safe, we design our APIs in a way to easily make the distinction between potentially user-provided data vs programmer-authored values, which we encode as literals (also known as compile-time constants in other languages).
The principal mechanism we use to programmatically encode literal values is tagged templates. This ensures that our API is easy to use as-is in JavaScript without relying on typing tricks or additional tooling.
Using Trusted Types in TypeScript still has a limitation as the standard lib has no awareness of Trusted Types. This means that you cannot assign a Trusted Type value to a sink directly.
While tsec
can recognise direct assignments to dangerous sinks,
we recommend using one of the dedicated wrappers from safevalues/dom
we
provide as they don't require you to cast the value.
import {sanitizeHtml} from 'safevalues';
import {safeElement} from 'safevalues/dom';
const el = document.createElement('div');
const html = sanitizeHtml('<article>my post <script>alert(0)</script></article>');
safeElement.setInnerHtml(el, html); // Trusted Type and tsec compatible
safevalues/dom
is Trusted Type compatible, and tsec compatible.
Certain DOM APIs which take URLs can be attacked and lead to XSS, when passed an
attacker controlled javascript:
URL. Trusted Types and CSP offer mechanism to
block navigations to javascript:
URLs
(Trusted Types' require-trusted-types-for
Pre-Navigation check,
CSP's default, disabled with unsafe-inline
).
Both these mechanisms require a compatible browser and app to be effective.
safevalues/dom
wrappers protect individual sink assignments and don't
require app level compatibility nor browser compatibility. Wrappers detect
javascript:
URL usages at assignment time, which is easier to detect than
exercising navigations in tests. They improve confidence that your app will be
compatible with Trusted Types and CSP's javascript:
navigation protections.
import {safeLocation} from 'safevalues/dom';
let userControlledUrl = 'https://github.com/google/safevalues';
safeLocation.setHref(document.location, userControlledUrl); // OK
userControlledUrl = 'javascript:evil()';
safeLocation.setHref(document.location, userControlledUrl); // Blocked
tsec
will - in the future - enforce that all DOM URL sinks are accessed using
the safevalues/dom
wrappers.
There are certain situations when migrating a codebase to be safe using Trusted Types can be difficult because it requires changing large parts of the code together or because the provided builders are too restrictive for some particular usage.
To help with these migrations, we provide two additional sets of functions that can reduce the impact of the issues above.
WARNING: Make sure you use tsec
to keep track of how your code is using these
functions.
More information: Restricted functions documentation.
This is not an officially supported Google product.