Skip to content

Commit

Permalink
polymer-resin: require an explicit install
Browse files Browse the repository at this point in the history
This makes it easier to run in an experiment framework.

I had hoped initially that I could simply provide an HTML importable module.

That doesn't work because of idiosyncracies of HTML import.
The solution to that is an empty script tag.
That empty script tag is less likely to be written out if there's something
in it, like a call to install.

This bundles several changes:
1. Adds security.polymer_resin.install
2. Reworks existing public API to work via a configuration object passed
   to insall.
3. Modifies existing tests to install.
4. Flesh out docs with install and the configuration.
Tested:
Reran project tests.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=157182380
  • Loading branch information
msamuel authored and mikesamuel committed May 26, 2017
1 parent ce8c3b3 commit 3e119dd
Show file tree
Hide file tree
Showing 15 changed files with 849 additions and 689 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,10 @@ to
```html
<script src="webcomponents-lite.js"></script>
<link rel="import" href="polymer-resin/polymer-resin.html">
<script>// This comment is essential to the security of this project.</script>
<script>
// This step is essential to the security of this project.
security.polymer_resin.install();
</script>
<link rel="import" href="custom-element.html">
```

Expand Down
1 change: 1 addition & 0 deletions a-tag-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="a-tag-test.js"></script>
<title>A Tag Tests</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions attr-property-aliasing-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="attr-property-aliasing-test.js"></script>
<title>Attr Property Aliasing Tests</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions classify-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="classify-test.js"></script>
<title>Classify Tests</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions computed-value-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="computed-value-test.js"></script>
<title>Computed Value Tests</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions custom-element-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="custom-element-test.js"></script>
<title>Custom Element Tests</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions enum-attribute-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="enum-attribute-test.js"></script>
<title>Enum Attribute Test</title>
</head>
Expand Down
124 changes: 95 additions & 29 deletions getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ bower install --save polymer-resin#1.2.3
### Downloading a release

If you don't want to use bower, you can browse releases of polymer-resin via the
project's [Github release
page](https://github.com/Polymer/polymer-resin/releases) and tarballs are
available there.
project's [Github release page][releases] and tarballs are available there.

### Integrating

Expand Down Expand Up @@ -84,8 +82,7 @@ Before | After
` ` | `. . <link rel="import"`
` ` | `. . . href="polymer-resin/polymer-resin.html" />`
` ` | `. . <script>`
` ` | `. . // Configure polymer-resin. See below.`
` ` | `. . // Must not be empty.`
` ` | `. . security.polymer_resin.install({ /*config*/ })`
` ` | `. . </script>`
`. . <link rel="import" href="my-app.html" />` | `. . <link rel="import" href="my-app.html" />`
`. <body>` | `. <body>`
Expand All @@ -99,41 +96,104 @@ importing, and must appear before polymer-resin is loaded.
The above Polymer document shows a synchronous script tag.

```html
<script>// Configure polymer-resin. Must not be empty.</script>
<script>
security.polymer_resin.install({ /* config */ })
</script>
```

Due to a quirk of how HTML imports work, this must be present even if you don't
need to do any configuration. This makes sure that the polymer-resin JS will
load before `<script src=...>`s loaded by subsequently imported modules.
Polymer resin will provide no protection until explicitly installed. This allows
it to be installed on an experimental basis for some subset of users during
migration.

The `{ /* config */ }` object can have a variety of properties..

Polymer-resin has a small API that allows granting privileges to application
code.
#### `{ 'allowedIdentifierPrefixes: ['prefix-'] }`

[`security.polymer_resin.allowIdentifierWithPrefix('prefix-')`][allow-ident-prefix]
allows data bindings to specify `id="prefix-..."` attribute values. Attacker
controlled IDs are not an [arbitrary-code execution vulnerability][a.c.e.] but
skilled attackers have exploited the fact that `document.getElementById(x)`
returns one of potentially many elements with the same ID. Ideally, an
application would make sure that important form input names and IDs controllable
by attackers are disjoint so that the content of inputs sent to the server
reflects user intent. If your application is not careful about IDs, and you want
to use polymer-resin to prevent arbitrary-code execution while you work on
separating ID namespaces, you can use the following:
`allowedIdentifierPrefixes` specifies an array of ID prefixes. This allows data
bindings to specify `id="prefix-..."` attribute values. If there are multiple
values, then an ID is allowed if it starts with any of the prefixes.

Attacker controlled IDs are not an [arbitrary-code execution
vulnerability][a.c.e.] but skilled attackers have exploited the fact that
`document.getElementById(x)` returns one of potentially many elements with the
same ID. Ideally, an application would make sure that important form input names
and IDs controllable by attackers are disjoint so that the content of inputs
sent to the server reflects user intent. If your application is not careful
about IDs, and you want to use polymer-resin to prevent arbitrary-code execution
while you work on separating ID namespaces, you can use the following:

```html
<script>
security.polymer_resin.allowIdentifierWithPrefix('');
security.polymer_resin.install({ allowedIdentifierPrefixes: [''] });
</script>
```

#### `{ 'reportHandler': myReportHandlerFn }`

`reportHandler` is a callback that receives reports about rejected values and
module status.

By default, if `goog.DEBUG` is false at init time, reportHandler is never
called, and if `goog.DEBUG` is true at init time, reportHandler logs to the JS
developer console.

Assuming it is enabled, either via `goog.DEBUG` or an explicit call to this
setter, then it is called on every rejected value, and on major events like
module initialization.

This may be used to identify false positives during debugging; to compile lists
of false positives when migrating; or to gather telemetry by compiling a table
summarizing disallowed value reports.

## <a name="migrating">Migrating an app or element to work with polymer-resin</a>

TODO: explain how one can load a variant of polymer-resin compiled in
`UNSAFE_ADVISORY_ONLY` mode. Polymer-resin doesn't actually substitute innocuous
values for unsafe inputs but logs and one can get a digest from the console of
rejected (element, attribute/property, value) triples which allows running tests
that don't include attempted attacks and seeing the kinds of false positives
that tend to show up.
When migrating an app to use polymer-resin, it can be helpful to get a list of
false positives. One false negative can cause a cascading security failure that
compromises your app, but false positives can also cause a cascading failure
that makes it hard to get coverage when manually testing an application.

```html
<script>
var polymerResinDebugTelemetry = {};
// Collect violation counts in a table instead of logging.
function telemetryGatheringReportHandler(
isDisallowedValue, fmtString,
optContextNodeName, optNodeName, optAttrName, optValue,
var_args) {
if (isDisallowedValue) {
var key = optContextNodeName + ' : ' + optNodeName + ' : '
+ optAttrName;
polymerResingDebugTelemetry[key] =
(polymerResingDebugTelemetry[key] || 0) + 1;
}
}
// Can be called from console.
function dumpPolymerResinDebugTelemetry() {
console.log(JSON.stringify(polymerResinDebugTelemetry, null, 2));
}
security.polymer_resin.install(
{
'reportHandler`: telemetryGatheringReportHandler
// Allow application to progress as normal so we can
// exercise as much of the app as possible without working
// around problems caused by false positives.
// HACK: DO NOT RUN IN PROD.
'UNSAFE_passThruDisallowedValues': true,
})
</script>
```
This configuration MUST NOT be used in production systems since
**UNSAFE_passThruDisallowedValues** disables the security protections similar to
[CSP Report-Only][csp-report-only] mode.
With this configuration, Polymer-resin doesn't actually substitute innocuous
values for unsafe inputs but collects them so that you can dump a digest to the
console.
## <a name="debugging">Debugging an app or element that uses polymer-resin</a>
Expand Down Expand Up @@ -223,6 +283,10 @@ Instead of using a contract type, code can often be refactored to do without.
This sample code could be refactored to use a `<button>` without any data
binding.
If you find that something is rejected that is innocuous, file a [bug][issues].
Polymer-resin whitelists elements and attributes, and our whitelist is probably
incomplete.
## End-to-end safety
TODO: talk about using in conjunction with JSConformance and `--polymer_pass` to
Expand All @@ -234,8 +298,10 @@ check sanitariness of JS and sources of safe html types.
[safe-url]: https://google.github.io/closure-library/api/goog.html.SafeUrl.html
[safe-html-types]: https://github.com/google/safe-html-types/blob/master/doc/safehtml-types.md
[html-import]: https://www.webcomponents.org/community/articles/introduction-to-html-imports
[allow-ident-prefix]: https://github.com/Polymer/polymer-resin/blob/6dbc44f9e5484771e483fdc0a3909f21eb1d99f9/polymer-resin.js#L51-L63
[a.c.e.]: https://en.wikipedia.org/wiki/Arbitrary_code_execution
[contracts-a-href]: https://github.com/Polymer/polymer-resin/blob/ff7f58f00ec0794517ecca11a801a2a7e6c04e84/lib/contracts/contracts.js#L296-L302
[closure-library]: https://github.com/google/closure-library
[contract-types]: https://github.com/google/safe-html-types/blob/master/doc/safehtml-types.md#types
[csp-report-only]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
[releases]: https://github.com/Polymer/polymer-resin/releases
[issues]: https://github.com/Polymer/polymer-resin/issues
4 changes: 3 additions & 1 deletion identifier-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
<script src="polymer-resin.js"></script>
<script src="identifier-test.js"></script>
<script>
security.polymer_resin.allowIdentifierWithPrefix('safe-');
security.polymer_resin.install({
'allowedIdentifierPrefixes': ['safe-']
});
</script>
<title>Identifier Test</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions one-attr-binding-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="one-attr-binding-test.js"></script>
<title>One Attr Binding Test</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions one-late-attr-binding-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<script src="/components/web-component-tester/browser.js"></script>
<link rel="import" href="/components/polymer/polymer.html" />
<script src="polymer-resin.js"></script>
<script>security.polymer_resin.install();</script>
<script src="one-late-attr-binding-test.js"></script>
<title>One Late Attr Binding Test</title>
</head>
Expand Down
Loading

0 comments on commit 3e119dd

Please sign in to comment.