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:
- Use
goog.dom
functions withgoog.dom.TagName
instances. - Use
getElementByTagNameAndClass
. - Use
goog.asserts.dom
if there’s no better API. - Avoid type-casting as there’s no check whether you actually cast a correct
type. For example, type-casting
HTMLScriptElement
as anElement
can lead it to being incorrectly treated as anHTMLImageElement
elsewhere.
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.
throwing non-error objects
Thrown objects that don’t extend Error
cannot have a stack trace attached to
them, making debugging significantly more difficult.
```javascript {.bad} throw ‘message’;
```javascript {.bad}
throw myObject;
Ensure that all thrown objects either extend Error
or are passed to the
Error
constructor:
```javascript {.good} throw new Error(‘message’);
```javascript {.good}
throw new Error(myObject);
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
.