web-contract-types

2.0.2 • Public • Published
Sisyphus Logo

Web Contract Types

Build Status Dependencies Status npm Coverage Status Install Size Known Vulnerabilities

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 '&lt;', and other HTML metacharacters replaced with similar character references.

const { TrustedHTML } = require('web-contract-types');
TrustedHTML.escape('Hello, <World!>').content === 'Hello, &lt;World!&gt;';

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 script
  • defer: 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.

Package Sidebar

Install

npm i web-contract-types

Weekly Downloads

4

Version

2.0.2

License

Apache-2.0

Unpacked Size

41.7 kB

Total Files

4

Last publish

Collaborators

  • mikesamuel