Closure Library

[TOC]

The config file: closure/goog/conformance_proto.txt

Introduction

Closure JavaScript code is expected to conform to a set of rules for security, performance, code health, or other reasons. This configuration file for the JS Conformance Framework enforces this.

The security-specific rules in here mostly target DOM (and Closure) APIs that are prone to script-injection vulnerabilities (XSS). In these cases, the rules will point to wrapper APIs that, instead of plain strings, consume values of types with specific security contracts indicating that the value can be safely used in a given context.

Possible Violations

If you are adding code and the warning you are seeing doesn’t seem appropriate and the warning is a “possible violation”, then the compiler doesn’t have enough type information to confirm that you aren’t violating a rule. As noted in the JS Conformance Framework documentation, conformance rules are enforced strictly so you aren’t allowed to “possibly violate”.

How to fix possible violations

Removing false-positive ‘possible violations’ requires providing more type information. Often this is as simple as declaring array content types, tightening an API’s return type, or choosing a different API.

For example, many Closure DOM APIs return a precise type if passed a goog.dom.TagName instance. Passing this instance instead of a string solves many possible violations.

Examples:

// Possible violation.
const img = goog.dom.createDom('img');
img.src = src;
// Clean.
const img = goog.dom.createDom(goog.dom.TagName.IMG);
img.src = src;
// Build error - native APIs don't support goog.dom.TagName.
const img = document.createElement(goog.dom.TagName.IMG);
img.src = src;
// Possible violation.
const img = goog.dom.getElementByClass('avatar');
img.src = src;
// Clean.
const img = goog.dom.getElementByTagNameAndClass(goog.dom.TagName.IMG, 'avatar');
img.src = src;
// Possible violation.
const img = goog.dom.getElement('avatar');
img.src = src;
// Clean.
const img = goog.asserts.dom.assertIsHtmlImageElement(goog.dom.getElement('avatar'));
img.src = src;
// No violation but unsafe - see below.
const img = /** @type {!HTMLImageElement} */ (goog.dom.getElement('avatar'));
img.src = src;

Summing it up:

Explanation of conformance rules

eval

eval is a security risk and is not allowed to be used. Since values passed to eval() are evaluated and executed as any ordinary JavaScript, it is not inherently safe to pass content to eval(). eval() is typically not necessary for ordinary programming.

IE’s execScript is also banned.

Function, setTimeout, setInterval and requestAnimationFrame with string argument are also banned.

throw ‘message’

throw with a string literal can not have a stack trace attached to it, making debugging significantly more difficult. Use throw new Error('message') instead.

Arguments.prototype.callee

Arguments.prototype.callee is not allowed in EcmaScript “strict mode” code.

Calls to Document.prototype.write

Calling Document.prototype.write is a security risk and is banned. Any content passed to write() will be automatically evaluated in the DOM, so the assignment of user-controlled, insufficiently sanitized or escaped content can result in XSS vulnerabilities.

Document.prototype.write is bad for performance as it forces document re-parsing, has unpredictable semantics and disallows many optimizations a browser may make. It is almost never needed. Only exception is writing to a completely new window such as a popup or an iframe.

If you need to use it, use the type-safe goog.dom.safe.documentWrite wrapper, or directly render a Strict Soy template using goog.soy.Renderer.prototype.renderElement (or similar).

Assignment to Element.prototype.innerHTML/outerHTML

Direct assignment of a non-constant value to innerHTML and outerHTML is a security risk and is banned. Any content passed to innerHTML or outerHTML will be automatically evaluated in the DOM and therefore the assignment of user-controlled, insufficiently sanitized or escaped content can result in XSS vulnerabilities.

Instead, use the type-safe goog.dom.safe.setInnerHtml wrapper, or directly render a Strict Soy template using goog.soy.Renderer.prototype.renderElement (or similar).

NOTE: Reads of these properties are permitted.

Creating untyped elements

We have several conformance rules banning assignment to dangerous properties such as script.src. These rules work only if we know the type of the manipulated element, e.g. HTMLScriptElement. Unfortunately, document.createElement('script') and similar APIs return only Element as perceived by the compiler. For our rules to work, we need to know the exact type which is returned by goog.dom methods when used together with goog.dom.TagName. Typically, it’s goog.dom.createElement and goog.dom.createDom, but other methods such as goog.dom.getElementsByTagName also work. DomHelper counterparts of these methods support goog.dom.TagName too.

For this reason, we ban creating untyped 'script', 'iframe', 'frame', 'embed', and 'object' elements and require using goog.dom methods with goog.dom.TagName with them.

Assignment to Location.prototype.href and Window.prototype.location

Direct assignment of a non-constant value to Location.prototype.href and Window.prototype.location is a security risk and is banned. Externally controlled strings assigned to Location.href can result in XSS vulnerabilities, e.g. via “javascript:evil()” URLs.

Instead of directly assigning to Location.prototype.href or Window.prototype.location, use the safe wrapper function goog.dom.safe.setLocationHref. When passed a string, this wrapper sanitizes the URL before passing it to the underlying DOM property. If passed a value of type goog.html.SafeUrl, the value is assigned without further sanitization.

NOTE: Reads of this property are permitted.

Assignment to .href property of Anchor, Link, etc elements

Direct assignment of a non-constant value to the href property of Anchor, Link, and similar elements is a security risk and is banned. Externally controlled strings assigned to the href property can result in XSS vulnerabilities, e.g. via “javascript:evil()” URLs.

Instead of directly assigning to the href property, use safe wrapper functions such as goog.dom.safe.setAnchorHref. When passed a string, this wrapper sanitizes the URL before passing it to the underlying DOM property. If passed a value of type goog.html.SafeUrl, the value is assigned without further sanitization.

NOTE: Reads of this property are permitted.

Assignment to property requires a TrustedResourceUrl via goog.dom.safe

Assignment of a non-constant value to certain URL-valued properties, like Base.href and Script.src, via a string that is not fully application controlled is a security risk and is banned. Attacker controlled values assigned to these properties can result in loading code from an untrusted domain. For example, the following would be unsafe if www.google.com were to have an open redirector and attackerControlled were something like '../redirect=http://evil.com/evil#':

script.src = 'https://www.google.com/module/' + attackerControlled + '.js';

Instead of directly assigning to these properties use safe wrapper functions which take TrustedResourceUrl, such as goog.dom.safe.setScriptSrc.

Note: Reads of this property are permitted.

Assigning a variable to a dangerous property via createDom is forbidden.

goog.dom.createDom and its version in DomHelper support assigning attributes to the newly created elements. This conformance rule bans assigning attributes that can load attacker controlled code, such as script.src or innerHTML.

To assign these attributes, create the element first and then assign the attribute using goog.dom.safe functions like this:

var script = goog.dom.createDom(goog.dom.TagName.SCRIPT);
goog.dom.safe.setScriptSrc(script, trustedResourceUrl);

Alternatively, use a function in goog.html.SafeHtml such as goog.html.SafeHtml.createScriptSrc.

This rule might report a possible violation if the tag name or attributes are not literals. To avoid this possible violation, structure the code like this:

// Reports a possible violation.
var tag = 'img';
var attrs = {'src': ''};
goog.dom.createDom(tag, attrs);
// Passes.
goog.dom.createDom('img', {'src': ''});

Note that string literal values assigned to banned attributes are allowed as they couldn’t be attacker controlled.

Setting content of Script element is not allowed

Setting content of <script> and then appending it to the document has the same effect as calling eval(). This coding pattern is prone to XSS vulnerabilities, and therefore disallowed.

Window.prototype.postMessage

Raw postMessage() does not restrict target and sender origins by default. This can cause security vulnerabilities.

For Search PA developers, see http://go/gws-js-conformance#postMessage for exemption instructions.

Global declarations

Global functions and var declarations are not allowed, as these pollute global scope. Top level namespaces are allowed if declared with “goog.provide” or “goog.module”.

Unknown types

Loose types ? (unknown), * (all), Object and Function should be used sparingly as they degrade available type information. ? as a “this” type is forbidden so that accidental unknowns (which are far more common) can be caught.

Client Side Storage (Closure library specific)

Client side storage mechanisms are dangerous because of PII and security implications.

Unsafe legacy APIs

Closure (as well as some libraries built on top of Closure) include several APIs that consume plain strings, and pass them on to an API that process that string in an injection-vulnerability-prone way (most commonly, an assignment to .innerHTML). Thus, use of such APIs incurs similar risks of injection vulnerabilities as the underlying DOM API (e.g., innerHTML assignment). Due to these risks, conformance rules disallow the use of such APIs. The respective conformance rules’ error message refers to the equivalent, safe API to use instead. Typically, the safe API consumes values of an appropriate security-contract type such as goog.html.SafeHtml.