web-contract-types
v2.0.2
Published
Contract types for common web application languages: HTML, JS, CSS, URL
Downloads
148
Maintainers
Readme
Web Contract Types
Security contract types for common web application languages: HTML, JavaScript, URLs.
Installation
$ npm install web-contract-types
Configuration
For applications
These types are Mintable so the application's main module should do some setup to guard which modules can create values that meet a contract.
This helps an application team, in conjunction with security specialists, keep a bound on how much code needs review to check that contracts hold.
The applications main file should do, as early as possible, something like:
// In application main file.
require('node-sec-patterns').authorize(require('./package.json'));
which opts into access checks for mintable type constructors, and
tells it to use the "mintable" property of ./package.json
to
determine which modules may create which contract types.
The APIs below require access to the module's own minters, so the minimal additions to package.json are
{
...
"mintable": {
"grants": {
"web-contract-types/TrustedHTML": [ "web-contract-types" ],
"web-contract-types/TrustedResourceURL": [ "web-contract-types" ],
"web-contract-types/TrustedScript": [ "web-contract-types" ],
"web-contract-types/TrustedURL": [ "web-contract-types" ]
}
}
}
This says "this application trusts module web-contract-types to mint values
that meet the contracts "web-contract-types/TrustedHTML", etc. This relies
on the fact that class TrustedHTML
has a static contractKey
property with
the value "web-contract-types/TrustedHTML"
.
This can be a bit verbose, so if you trust the web-contract-types project and its development practices, you can second any grants that it self-nominates for:
{
...
"mintable": {
"grants": {},
"second": [
"web-contract-types"
]
}
}
This says "for each item in
require('web-contract-types/package.json').mintable.selfNominate
add
"web-contract-types"
to that contract keys grant list".
If the seconded name ends in .json
then /package.json
is not implicitly
added to the end, so module authors might provide self-nominates for differing
levels of trust.
To see what this grants you can do the below, but keep in mind that a package might change its self nominations in future versions so by seconding self-nominated grants you are expressing confidence in future development practices:
$ node -e 'console.log(JSON.stringify(require("web-contract-types/package.json").mintable.selfNominate, null, 2))'
See Mintable for more details.
For library authors
Library code should not call authorize
as in the example code for
application maintainers above.
Library code may self nominate by including a list of contract keys that the package needs to mint values for. In package.json
{
...
"mintable": {
"selfNominate": [
"contractKey0",
"contractKey1"
]
}
}
Library authors are responsible for guaranteeing that they only mint values that meet the type's contract even when passed untrusted inputs.
Library authors may assume that any inputs that pass a mintable types verifier pass that type's contract, and are not responsible for failure to preserve a contract given a verified input that does not meet its type contract.
See Mintable for more details.
Contracts
TrustedHTML
A string that is safe to use in HTML context in DOM APIs and HTML documents.
A TrustedHTML is a string-like object that carries the security type contract that its value as a string will not cause untrusted script execution when evaluated as HTML in a browser.
Values of this type are guaranteed to be safe to use in HTML contexts, such as, assignment to the innerHTML DOM property, or interpolation into a HTML template in HTML PC_DATA context, in the sense that the use will not result in a Cross-Site-Scripting vulnerability.
Instances must be created by Mintable.minterFor(TrustedHTML)
.
When checking types, use Mintable.verifierFor(TrustedHTML)
and do not rely on
instanceof
.
TrustedResourceURL
A URL which is under application control and from which script, CSS, and other resources that represent executable code, can be fetched.
Given that the URL can only be constructed from strings under application control and is used to load resources, bugs resulting in a malformed URL should not have a security impact and are likely to be easily detectable during testing. Given the wide number of non-RFC compliant URLs in use, stricter validation could prevent some applications from being able to use this type.
Instances must be created by Mintable.minterFor(TrustedResourceURL)
.
When checking types, use Mintable.verifierFor(TrustedResourceURL)
and do
not rely on instanceof
.
TrustedScript
A string-like object which represents JavaScript code and that carries the security type contract that its value, as a string, will not cause execution of unconstrained attacker controlled code (XSS) when evaluated as JavaScript in a browser.
A TrustedScript's string representation can safely be interpolated as the content of a script element within HTML. The TrustedScript string should not be escaped before interpolation.
Note that the TrustedScript might contain text that is attacker-controlled but that text should have been interpolated with appropriate escaping, sanitization and/or validation into the right location in the script, such that it is highly constrained in its effect (for example, it had to match a set of whitelisted words).
Instances must be created by Mintable.minterFor(TrustedScript)
.
When checking types, use Mintable.verifierFor(TrustedScript)
and do
not rely on instanceof
.
TrustedURL
A string that is safe to use in URL context in DOM APIs and HTML documents.
A TrustedURL is a string-like object that carries the security type contract that its value as a string will not cause untrusted script execution when evaluated as a hyperlink URL in a browser.
Values of this type are guaranteed to be safe to use in URL/hyperlink contexts, such as assignment to URL-valued DOM properties, in the sense that the use will not result in a Cross-Site-Scripting vulnerability. Similarly, TrustedURLs can be interpolated into the URL context of an HTML template (e.g., inside a href attribute). However, appropriate HTML-escaping must still be applied.
Instances must be created by Mintable.minterFor(TrustedURL)
.
When checking types, use Mintable.verifierFor(TrustedURL)
and do not rely on
instanceof
.
Creating Trusted values
require('module-keys/cjs').polyfill(module, require, module.id);
const { Mintable } = require('node-sec-patterns');
const { TrustedHTML } = require('web-contract-types');
const makeTrustedHTML = require.moduleKeys.unbox(
Mintable.minterFor(TrustedHTML),
() => true,
String);
This boilerplate can be tiresome, but this should only happen in an applications secure kernel.
Do not grant access to makeTrustedHTML
widely. That defeats the purpose of
guarding constructors to minimize the amount of code that could result in a
security vulnerability.
See Mintable for more details.
Verifying Trusted values
Any JavaScript code that can access a class can create an object that
is an instanceof
that class.
To prevent accepting a contract forged by code outside your secure kernel, check types thus:
const { TrustedHTML } = require('web-contract-types');
if (TrustedHTML.is(x)) {
// x is not a forgery
// May assume x meets its type contract.
} else {
// Do not assume x meets the TrustedHTML type contract.
}
API
class TrustedHTML
The contract type for TrustedHTML. See contract above.
TrustedHTML.concat
const { TrustedHTML } = require('web-contract-types');
TrustedHTML.concat(x, y, z);
Takes any number of TrustedHTML inputs and returns a TrustedHTML output whose content is the concatenation of the inputs' content.
Throws a TypeError if any input does not verify as TrustedHTML
TrustedHTML.empty
A TrustedHTML instance that represents the empty document fragment.
const { TrustedHTML } = require('web-contract-types');
TrustedHTML.empty;
TrustedHTML.escape
Given a string, returns a TrustedHTML instance that represents a text node with that textContent.
Given a TrustedHTML instance, returns it unchanged.
The content is equivalent to the input but with '<'
replaced with '<'
,
and other HTML metacharacters replaced with similar character references.
const { TrustedHTML } = require('web-contract-types');
TrustedHTML.escape('Hello, <World!>').content === 'Hello, <World!>';
TrustedHTML.fromScript
const { TrustedHTML } = require('web-contract-types');
TrustedHTML.fromScript(myTrustedResourceURL)
Given a TrustedResourceURL, returns a TrustedHTML
instance like <script src=...></script>
.
Given a TrustedScript, returns a TrustedHTML
instance like <script>...</script>
.
May also take a second options argument that allows specifying:
type
: May be "module" to specify that the src is an ES6 module not a scriptdefer
: If truthy, the output script element has the defer attribute.async
: If truthy, the output script element has the async attribute.nonce
: Unescaped text of a Conent-Security-Policy nonce.
class TrustedResourceURL
The contract type for TrustedResourceURL. See contract above.
TrustedResourceURL.fromScript
const { TrustedResourceURL } = require('web-contract-types');
TrustedResourceURL.fromScript(myTrustedScript)
// ~ data:text/javascript,...
If the input is a TrustedScript returns a TrustedResourceUrl with scheme data:
,
content type text/javascript, and a data segment that is the script's content.
If the input is not a TrustedScript, throws a TypeError.
class TrustedScript
The contract type for TrustedScript. See contract above.
TrustedScript.expressionFromJSON
const { TrustedScript } = require('web-contract-types');
const dataObject = { "foo": [ "bar" ] };
TrustedScript.expressionFromJSON(dataObject)
// ~ ({ "foo", [ "bar" ] })
Forwards its arguments to JSON.stringify
and returns a TrustedScript
whose content is a parenthesized JavaScript expression that produces
similar data.
It forwards all arguments, so accepts the same optional arguments
as JSON.stringify
.
- value
- replacer
- space
It throws an exception when JSON.stringify
does -- for example, reference cycles.
class TrustedURL
The contract type for TrustedURL. See contract above.
TrustedURL.innocuousURL
const { TrustedURL } = require('web-contract-types');
TrustedURL.innocuousURL
A URL that will have no effect when loaded. May be used as a placeholder.
TrustedURL.sanitize
const { TrustedURL } = require('web-contract-types');
TrustedURL.sanitize('http://example.com/').content === 'http://example.com';
Given a string, returns a TrustedURL with that string's content if the string is a relative URL or has a scheme in
- http
- https
- mailto
- tel
Given a TrustedURL returns its input unchanged.
If the input does not pass one of the given conditions, returns its second
argument unchanged, or if that argument is falsey, returns TrustedURL.innocuousURL
.