From 499d668d615083dee2412bf1081d51b5332b4d11 Mon Sep 17 00:00:00 2001 From: Jeremy Stashewsky Date: Thu, 21 Nov 2013 17:22:15 -0800 Subject: [PATCH 1/2] Jade PoC test --- package.json | 3 ++- test.js | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ca08c3..f55167a 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "component": "0.18.0", "coveralls": "2.3.0", "mocha-lcov-reporter": "0.0.1", - "blanket": "1.1.5" + "blanket": "1.1.5", + "jade": "~0.35.0" } } diff --git a/test.js b/test.js index 38184ab..7cb2cfc 100644 --- a/test.js +++ b/test.js @@ -324,4 +324,44 @@ describe('exporting to EJS', function() { }); }); +var jade; +try { + jade = require('jade'); +} catch (e) { +} + +if (jade) { + describe('jade integration', function() { + describe('default escape', function() { + it('has not-so-safe escaping before', function() { + var result = jade.render('a(href=foo)',{foo: 'some="where"'}); + assert.equal(result, ''); + }); + it('has slightly better escaping after', function() { + var origEscape = jade.runtime.escape; + jade.runtime.escape = secureFilters.html; + try { + var result = jade.render('a(href=foo)',{foo: 'some="where"'}); + assert.equal(result, ''); + } finally { + jade.runtime.escape = origEscape; + } + }); + }); + + describe('custom escaping', function() { + it('jsObj interpolation', function() { + var template = 'script.\n' + + ' var name = !{jsObj(name)};\n'; + var opts = { + jsObj: secureFilters.jsObj, + name: '' + }; + var result = jade.render(template, opts); + assert.equal(result, ''); + }); + }); + }); +} + }(this)); From 2ff11c63932c95b854f52bc6f841c445f36a568f Mon Sep 17 00:00:00 2001 From: Jeremy Stashewsky Date: Thu, 21 Nov 2013 17:36:08 -0800 Subject: [PATCH 2/2] Document how to set up with Jade --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++ lib/secure-filters.js | 15 ++++++++-- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94868f2..9986844 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Table of select contents: - [Usage](#usage) - [Installation](#installation) - `npm install --save secure-filters` - [EJS](#with-ejs) + - [Jade](#with-jade) - [Normal functions](#as-normal-functions) - [Client-side](#client-side) - [Functions](#functions) @@ -24,6 +25,9 @@ Table of select contents: - [`uri(value)`](#urivalue) - Sanitizes URI contexts using percent-encoding. - [`css(value)`](#cssvalue) - Sanitizes CSS contexts using backslash-encoding. - [`style(value)`](#stylevalue) - Sanitizes CSS contexts _in an HTML `style` attribute_ +- [Utility Functions](#utility) + - [`configure(ejs)`](#configureejs) + - [`inject(obj)`](#injectobj) - [Contributing](#contributing) - [Support](#support) - [Legal](#legal) @@ -161,6 +165,40 @@ Or, you can namespace using a parametric style, similar to how EJS' pre-defined ``` +## With Jade + +Integrating with [Jade](http://jade-lang.org) isn't quite as easy as EJS, but +still somewhat readable. + +```js + var config = { loggedIn: true }; + var locals = secureFilters.inject({ + config: config + }); + jade.render(template, locals); +``` + +And in the template you use the `!{}` interpolation operator + +```jade + script. + var config = !{jsObj(config)}; +``` + +Produces + +```html + +``` + +### Replacing Jade's default escape + +Replacing Jade's default escaping is easy + +```js + jade.runtime.escape = secureFilters.html; +``` + ## As Normal Functions The filter functions are just regular functions and can be used outside of EJS. @@ -191,6 +229,8 @@ don't pre-define a name, but suggest that you use 'secure-filters'. # Functions +## Filters + By convention in the Contexts below, `USERINPUT` should be replaced with the output of the filter function. @@ -405,6 +445,32 @@ Encodes the value first as in the `css()` filter, then HTML entity-encodes the r For example, the string `` becomes `\3c wow\3e `. +## Utility + +### configure(ejs) + +Configures EJS with the filter functions in this module. Returns the `ejs` module you passed in. + +```js + var ejs = require('ejs'); + ejs = secureFilters.configure(ejs); +``` + +Note that this doesn't modify EJS's default escape routine. [See +above](#replacing-ejss-default-escape) for instructions on how to do this. + +### inject(obj) + +Adds all of the filter functions in this module into an object. Returns the same, modified object. + +```js + var locals = { + name: 'bob' + }; + secureFilters.inject(locals); + someTemplateLang.render(template, locals); +``` + # Contributing If you'd like to contribute to or modify secure-filters, here's a quick guide diff --git a/lib/secure-filters.js b/lib/secure-filters.js index de4ff97..084ecc2 100644 --- a/lib/secure-filters.js +++ b/lib/secure-filters.js @@ -36,11 +36,22 @@ var TO_CONFIGURE = ['html','js','jsAttr','uri','jsObj','css','style']; */ secureFilters.configure = function(ejs) { ejs.filters = ejs.filters || {}; + secureFilters.inject(ejs.filters); + return ejs; +}; + +/** + * Adds this module's filters to an arbitrary object. + * + * @param {Object} obj any writable object + * @return {Object} the same, modified, object + */ +secureFilters.inject = function(obj) { for (var i = 0; i < TO_CONFIGURE.length; i++) { var filterName = TO_CONFIGURE[i]; - ejs.filters[filterName] = secureFilters[filterName]; + obj[filterName] = secureFilters[filterName]; } - return ejs; + return obj; }; var QUOT = /\x22/g; // "