From 3df30834ab666747032159a50f444bbb32154919 Mon Sep 17 00:00:00 2001 From: Patrick Steele-Idem Date: Tue, 12 Jan 2016 14:37:07 -0700 Subject: [PATCH] Fixes #93 - Update lasso to add support for CSP nonce --- README.md | 78 +++++++++- lib/Config.js | 5 + lib/Lasso.js | 4 +- lib/LassoPageResult.js | 85 +++++++++-- lib/Slot.js | 7 +- lib/config-loader.js | 8 + marko-taglib.json | 10 ++ package.json | 140 +++++++++--------- taglib/LassoRenderContext.js | 5 + taglib/helper-getNonce.js | 11 ++ taglib/lasso-nonce-attr-transformer.js | 16 ++ taglib/page-tag.js | 6 +- taglib/slot-tag.js | 35 +++-- test/lasso-test.js | 30 ++++ test/taglib-test.js | 20 +++ .../src/pages/csp-test/browser.json | 8 + .../src/pages/csp-test/favicon.ico | Bin 0 -> 1150 bytes .../src/pages/csp-test/marko-logo.png | Bin 0 -> 31647 bytes .../src/pages/csp-test/style-inline.css | 3 + .../test-project/src/pages/csp-test/style.css | 3 + .../src/pages/csp-test/template.dust | 10 ++ .../csp-test/template.dust.expected.html | 1 + .../src/pages/csp-test/template.marko | 15 ++ .../csp-test/template.marko.expected.html | 5 + .../src/pages/csp-test/test-inline.js | 1 + test/test-project/src/pages/csp-test/test.js | 1 + 26 files changed, 406 insertions(+), 101 deletions(-) create mode 100644 taglib/helper-getNonce.js create mode 100644 taglib/lasso-nonce-attr-transformer.js create mode 100644 test/test-project/src/pages/csp-test/browser.json create mode 100644 test/test-project/src/pages/csp-test/favicon.ico create mode 100644 test/test-project/src/pages/csp-test/marko-logo.png create mode 100644 test/test-project/src/pages/csp-test/style-inline.css create mode 100644 test/test-project/src/pages/csp-test/style.css create mode 100644 test/test-project/src/pages/csp-test/template.dust create mode 100644 test/test-project/src/pages/csp-test/template.dust.expected.html create mode 100644 test/test-project/src/pages/csp-test/template.marko create mode 100644 test/test-project/src/pages/csp-test/template.marko.expected.html create mode 100644 test/test-project/src/pages/csp-test/test-inline.js create mode 100644 test/test-project/src/pages/csp-test/test.js diff --git a/README.md b/README.md index f8539a8c..008da171 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ There's also a JavaScript API, taglib and a collection of plugins to make your j - [Complete Configuration](#complete-configuration) - [Node.js-style Module Support](#nodejs-style-module-support) - [No Conflict Builds](#no-conflict-builds) +- [Content Security Policy Support](#content-security-policy-support) - [Available Plugins](#available-plugins) - [Extending Lasso.js](#extending-lassojs) - [Custom Plugins](#custom-plugins) @@ -161,7 +162,6 @@ There's also a JavaScript API, taglib and a collection of plugins to make your j * Optional Base64 image encoding inside CSS files * Custom output transforms * Declarative browser-side package dependencies using simple `browser.json` files - * Generates the HTML markup required to include bundled resources * Conditional dependencies * Image minification * etc. @@ -174,6 +174,7 @@ There's also a JavaScript API, taglib and a collection of plugins to make your j * Full support for [browserify](http://browserify.org/) shims and transforms * Maintains line numbers in wrapped code * Developer Friendly + * Generates the HTML markup required to include bundled resources * Disable bundling and minification in development * Line numbers are maintained for Node.js modules source * Extremely fast _incremental builds_! @@ -199,6 +200,8 @@ There's also a JavaScript API, taglib and a collection of plugins to make your j * Integrate with build tools * Use with Express or any other web development framework * JavaScript API, CLI and taglib +* Security + * Supports the [nonce attribute](https://www.w3.org/TR/CSP2/#script-src-the-nonce-attribute) when using Content Security Policy for extra security. * _Future_ * Automatic image sprites @@ -1359,6 +1362,79 @@ require('lasso').create({ See [Configuration](#configuration) for full list of configuration options. +# Content Security Policy Support + +Newer browsers support a web standard called Content Security Policy that prevents, among other things, cross-site scripting attacks by whitelisting inline ` + + + + +``` + +NOTE: A `nonce` attribute is only added to inline ` + +``` + +The output HTML will be similar to the following: + +```html + + +``` # Available Plugins diff --git a/lib/Config.js b/lib/Config.js index 6d304cb9..37509e7c 100644 --- a/lib/Config.js +++ b/lib/Config.js @@ -150,6 +150,7 @@ function Config(params) { this.cacheDir = null; this._requirePluginConfig = {}; this._imagePluginConfig = {}; + this.cspNonceProvider = null; if (params) { extend(this.params, params); @@ -383,6 +384,10 @@ Config.prototype = { getBundleReadTimeout: function() { return this.bundleReadTimeout; + }, + + setCSPNonceProvider: function(func) { + this.cspNonceProvider = func; } }; diff --git a/lib/Lasso.js b/lib/Lasso.js index 6e71f5f9..220eabea 100644 --- a/lib/Lasso.js +++ b/lib/Lasso.js @@ -576,11 +576,11 @@ Lasso.prototype = { }, getJavaScriptDependencyHtml: function(url) { - return ''; + return ''; }, getCSSDependencyHtml: function(url) { - return ''; + return ''; }, _resolveflags: function(options) { diff --git a/lib/LassoPageResult.js b/lib/LassoPageResult.js index 6561b12f..429321e6 100644 --- a/lib/LassoPageResult.js +++ b/lib/LassoPageResult.js @@ -1,12 +1,30 @@ var extend = require('raptor-util/extend'); +var marko = require('marko'); +var nodePath = require('path'); +var EMPTY_OBJECT = {}; + +function generateTempFilename(slotName) { + // Generates a unique filename based on date/time and, process ID and a random number + var now = new Date(); + return [ + slotName, + now.getYear(), + now.getMonth(), + now.getDate(), + process.pid, + (Math.random() * 0x100000000 + 1).toString(36) + ].join('-') + '.marko'; +} function LassoPageResult() { - this.htmlBySlot = {}; this.urlsBySlot = {}; this.urlsByContentType = {}; this.filesByContentType = {}; this.infoByBundleName = {}; this.infoByAsyncBundleName = {}; + this._htmlBySlot = {}; + + this._htmlTemplatesBySlot = {}; } LassoPageResult.deserialize = function(reader, callback) { @@ -51,8 +69,16 @@ LassoPageResult.prototype = { * * @return {Object} An object with slot names as property names and slot HTML as property values. */ - getHtmlBySlot: function() { - return this.htmlBySlot; + get htmlBySlot() { + var htmlBySlot = {}; + for (var slotName in this._htmlBySlot) { + if (this._htmlBySlot.hasOwnProperty(slotName)) { + var slotHtml = this.getHtmlForSlot(slotName); + htmlBySlot[slotName] = slotHtml; + } + } + + return htmlBySlot; }, /** @@ -64,26 +90,54 @@ LassoPageResult.prototype = { * * * @param {String} slotName The name of the slot (e.g. "head" or "body") + * @param {Object} data Input data to the slot that is used to render the actual slot HTML * @return {String} The HTML for the slot or an empty string if there is no HTML defined for the slot. */ - getHtmlForSlot: function(slotName) { - return this.htmlBySlot[slotName] || ''; + getHtmlForSlot: function(slotName, data) { + var template = this._getSlotTemplate(slotName); + if (!template) { + return ''; + } + return template.renderSync(data || EMPTY_OBJECT); }, + _getSlotTemplate: function(slotName) { + var template = this._htmlTemplatesBySlot[slotName]; + if (!template) { + var templateSrc = this._htmlBySlot[slotName]; + if (!templateSrc) { + return null; + } - getHeadHtml: function() { - return this.getHtmlForSlot('head'); + // In order to compile the HTML for the slot into a Marko template, we need to provide a faux + // template path. The path doesn't really matter unless the compiled template needs to import + // external tags or templates. + var templatePath = nodePath.resolve(__dirname, '..', generateTempFilename(slotName)); + template = marko.load(templatePath, templateSrc, { preserveWhitespace: true }); + // Cache the loaded template: + this._htmlTemplatesBySlot[slotName] = template; + + // The Marko template compiled to JS and required. Let's delete it out of the require cache + // to avoid a memory leak + delete require.cache[templatePath + '.js']; + } + + return template; }, - getBodyHtml: function() { - return this.getHtmlForSlot('body'); + getHeadHtml: function(data) { + return this.getHtmlForSlot('head', data); + }, + + getBodyHtml: function(data) { + return this.getHtmlForSlot('body', data); }, /** * Synonym for {@Link raptor/lasso/LassoPageResult#getHtmlForSlot} */ - getSlotHtml: function(slot) { - return this.getHtmlForSlot(slot); + getSlotHtml: function(slotName, data) { + return this.getHtmlForSlot(slotName, data); }, /** @@ -94,8 +148,15 @@ LassoPageResult.prototype = { return JSON.stringify(this.htmlBySlot, null, indentation); }, + toJSON: function() { + var clone = extend({}, this); + // Don't include the loaded templates when generating a JSON string + delete clone._htmlTemplatesBySlot; + return clone; + }, + setHtmlBySlot: function(htmlBySlot) { - this.htmlBySlot = htmlBySlot; + this._htmlBySlot = htmlBySlot; }, registerBundle: function(bundle, async, lassoContext) { diff --git a/lib/Slot.js b/lib/Slot.js index 0b085a9a..c90bd68a 100644 --- a/lib/Slot.js +++ b/lib/Slot.js @@ -4,7 +4,6 @@ function Slot(contentType) { ok(contentType, 'contentType is required'); this.contentType = contentType; this.content = []; - } Slot.prototype = { @@ -30,16 +29,16 @@ Slot.prototype = { code: content }); }, - + buildHtml: function() { var output = []; for (var i=0, len=this.content.length; i' + content.code + ''); + output.push(''); } else if (this.contentType === 'css') { - output.push(''); + output.push(''); } } else { output.push(content.code); diff --git a/lib/config-loader.js b/lib/config-loader.js index 19a1cc0f..54dd73f2 100644 --- a/lib/config-loader.js +++ b/lib/config-loader.js @@ -364,6 +364,14 @@ function load(options, baseDir, filename, configDefaults) { // dependencies. config._requirePluginConfig.unbundledTargetPrefix = value; } + }, + + cspNonceProvider: function(value) { + if (typeof value !== 'function') { + throw new Error('"cspNonceProvider" should be a function'); + } + + config.setCSPNonceProvider(value); } }; diff --git a/marko-taglib.json b/marko-taglib.json index 7e718a3a..8380a001 100644 --- a/marko-taglib.json +++ b/marko-taglib.json @@ -90,6 +90,16 @@ "transformer": { "path": "./taglib/lasso-resource-tag-transformer" } + }, + "*": { + "attributes": { + "lasso-nonce": { + "ignore": true + } + }, + "transformer": { + "path": "./taglib/lasso-nonce-attr-transformer" + } } } } diff --git a/package.json b/package.json index c3012945..b2400131 100644 --- a/package.json +++ b/package.json @@ -1,71 +1,71 @@ { - "name": "lasso", - "description": "Lasso.js is a build tool and runtime library for building and bundling all of the resources needed by a web application", - "repository": { - "type": "git", - "url": "https://github.com/lasso-js/lasso.git" - }, - "scripts": { - "test": "rm -rf .cache && node_modules/.bin/mocha --timeout 4000 --ui bdd --reporter spec ./test && node_modules/.bin/mocha --timeout 4000 --ui bdd --reporter spec ./test && node_modules/.bin/jshint lib/ taglib/" - }, - "author": "Patrick Steele-Idem ", - "maintainers": "Patrick Steele-Idem ", - "dependencies": { - "app-module-path": "^1.0.0", - "app-root-dir": "^1.0.0", - "async": "^0.9.0", - "browser-refresh-client": "^1.0.0", - "glob": "^4.0.6", - "jsonminify": "~0.2.3", - "lasso-image": "^1.0.9", - "lasso-minify-css": "^1.0.0", - "lasso-minify-js": "^1.1.4", - "lasso-require": "^1.4.0", - "lasso-resolve-css-urls": "^1.0.0", - "mime": "~1.2.7", - "mkdirp": "^0.5.0", - "property-handlers": "^1.0.0", - "raptor-async": "^1.1.0", - "raptor-cache": "^1.1.1", - "raptor-detect": "^1.0.0", - "raptor-dust": "^1.1.2", - "raptor-logging": "^1.0.1", - "raptor-modules": "^1.0.5", - "raptor-objects": "^1.0.1", - "raptor-polyfill": "^1.0.0", - "raptor-promises": "^1.0.1", - "raptor-regexp": "^1.0.0", - "raptor-strings": "^1.0.0", - "raptor-util": "^1.0.0", - "resolve-from": "^1.0.0", - "send": "^0.13.0", - "through": "^2.3.4" - }, - "devDependencies": { - "babel-core": "^6.0.20", - "babel-preset-es2015": "^6.0.15", - "chai": "~1.8.1", - "deamdify": "^0.1.1", - "dustjs-linkedin": "^2.4.0", - "fs-extra": "~0.12.0", - "jshint": "~2.3.0", - "lasso-dust": "^1.1.3", - "lasso-marko": "^2.0.1", - "marko": "^2.7.25", - "mocha": "~1.15.1" - }, - "license": "Apache License v2.0", - "main": "lib/index.js", - "publishConfig": { - "registry": "https://registry.npmjs.org/" - }, - "keywords": [ - "bundler", - "build", - "css", - "javascript", - "concat", - "minify" - ], - "version": "1.13.0" -} \ No newline at end of file + "name": "lasso", + "description": "Lasso.js is a build tool and runtime library for building and bundling all of the resources needed by a web application", + "repository": { + "type": "git", + "url": "https://github.com/lasso-js/lasso.git" + }, + "scripts": { + "test": "rm -rf .cache && node_modules/.bin/mocha --timeout 4000 --ui bdd --reporter spec ./test && node_modules/.bin/mocha --timeout 4000 --ui bdd --reporter spec ./test && node_modules/.bin/jshint lib/ taglib/" + }, + "author": "Patrick Steele-Idem ", + "maintainers": "Patrick Steele-Idem ", + "dependencies": { + "app-module-path": "^1.0.0", + "app-root-dir": "^1.0.0", + "async": "^0.9.0", + "browser-refresh-client": "^1.0.0", + "glob": "^4.0.6", + "jsonminify": "~0.2.3", + "lasso-image": "^1.0.9", + "lasso-minify-css": "^1.0.0", + "lasso-minify-js": "^1.1.4", + "lasso-require": "^1.4.0", + "lasso-resolve-css-urls": "^1.0.0", + "marko": "^2.8.0", + "mime": "~1.2.7", + "mkdirp": "^0.5.0", + "property-handlers": "^1.0.0", + "raptor-async": "^1.1.0", + "raptor-cache": "^1.1.1", + "raptor-detect": "^1.0.0", + "raptor-dust": "^1.1.2", + "raptor-logging": "^1.0.1", + "raptor-modules": "^1.0.5", + "raptor-objects": "^1.0.1", + "raptor-polyfill": "^1.0.0", + "raptor-promises": "^1.0.1", + "raptor-regexp": "^1.0.0", + "raptor-strings": "^1.0.0", + "raptor-util": "^1.0.0", + "resolve-from": "^1.0.0", + "send": "^0.13.0", + "through": "^2.3.4" + }, + "devDependencies": { + "babel-core": "^6.0.20", + "babel-preset-es2015": "^6.0.15", + "chai": "~1.8.1", + "deamdify": "^0.1.1", + "dustjs-linkedin": "^2.4.0", + "fs-extra": "~0.12.0", + "jshint": "~2.3.0", + "lasso-dust": "^1.1.3", + "lasso-marko": "^2.0.1", + "mocha": "~1.15.1" + }, + "license": "Apache License v2.0", + "main": "lib/index.js", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "keywords": [ + "bundler", + "build", + "css", + "javascript", + "concat", + "minify" + ], + "version": "1.14.0" +} diff --git a/taglib/LassoRenderContext.js b/taglib/LassoRenderContext.js index c8f0ec02..cf345da7 100644 --- a/taglib/LassoRenderContext.js +++ b/taglib/LassoRenderContext.js @@ -44,6 +44,11 @@ LassoRenderContext.prototype = { getWaitFor: function() { return this._waitFor; + }, + + getLassoConfig: function() { + var theLasso = this.data.lasso; + return theLasso.config; } }; diff --git a/taglib/helper-getNonce.js b/taglib/helper-getNonce.js new file mode 100644 index 00000000..42b8ca41 --- /dev/null +++ b/taglib/helper-getNonce.js @@ -0,0 +1,11 @@ +var util = require('./util'); + +module.exports = function(out) { + var lassoRenderContext = util.getLassoRenderContext(out); + var lassoConfig = lassoRenderContext.getLassoConfig(); + + var cspNonceProvider = lassoConfig.cspNonceProvider; + if (cspNonceProvider) { + return cspNonceProvider(out, lassoRenderContext); + } +}; \ No newline at end of file diff --git a/taglib/lasso-nonce-attr-transformer.js b/taglib/lasso-nonce-attr-transformer.js new file mode 100644 index 00000000..eaad0299 --- /dev/null +++ b/taglib/lasso-nonce-attr-transformer.js @@ -0,0 +1,16 @@ +var getNonceHelperPath = require.resolve('./helper-getNonce'); + +module.exports = function transform(node, compiler, template) { + if (node.hasAttribute('lasso-nonce')) { + + node.removeAttribute('lasso-nonce'); + + var getNonceRequirePath = template.getRequirePath(getNonceHelperPath); + + template.addStaticVar('__getNonce', + + 'require("' + getNonceRequirePath + '")'); + + node.setAttribute('nonce', template.makeExpression('__getNonce(out)')); + } +}; \ No newline at end of file diff --git a/taglib/page-tag.js b/taglib/page-tag.js index 25cded27..baa4b4da 100644 --- a/taglib/page-tag.js +++ b/taglib/page-tag.js @@ -7,14 +7,14 @@ var fs = require('fs'); var AsyncValue = require('raptor-async/AsyncValue'); var extend = require('raptor-util/extend'); -module.exports = function render(input, context) { +module.exports = function render(input, out) { var theLasso = input.lasso; if (!theLasso) { theLasso = lasso.defaultLasso; } - var lassoRenderContext = util.getLassoRenderContext(context); + var lassoRenderContext = util.getLassoRenderContext(out); var pageName = input.name || input.pageName; var cacheKey = input.cacheKey; @@ -40,7 +40,7 @@ module.exports = function render(input, context) { // may be needed to build the page correctly. Ultimately, during the optimization // phase, this data can be access using the "lassoContext.data" property var lassoContextData = { - renderContext: context + renderContext: out }; // The user of the tag may have also provided some additional data to add diff --git a/taglib/slot-tag.js b/taglib/slot-tag.js index 06aff958..a40f050a 100644 --- a/taglib/slot-tag.js +++ b/taglib/slot-tag.js @@ -1,18 +1,35 @@ var util = require('./util'); -function renderSlot(slotName, lassoPageResult, context, lassoContext) { - var slotHtml = lassoPageResult.getSlotHtml(slotName); + +function renderSlot(slotName, lassoPageResult, out, lassoRenderContext) { + + var lassoConfig = lassoRenderContext.getLassoConfig(); + + var cspNonceProvider = lassoConfig.cspNonceProvider; + var slotData = null; + + if (cspNonceProvider) { + var cspAttrs = { + nonce: cspNonceProvider(out) + }; + + slotData = { + inlineScriptAttrs: cspAttrs, + inlineStyleAttrs: cspAttrs + }; + } + var slotHtml = lassoPageResult.getSlotHtml(slotName, slotData); if (slotHtml) { - context.write(slotHtml); + out.write(slotHtml); } - lassoContext.emitAfterSlot(slotName, context); + lassoRenderContext.emitAfterSlot(slotName, out); } -module.exports = function render(input, context) { +module.exports = function render(input, out) { var slotName = input.name; - var lassoRenderContext = util.getLassoRenderContext(context); + var lassoRenderContext = util.getLassoRenderContext(out); var lassoPageResultAsyncValue = lassoRenderContext.data.lassoPageResult; var timeout = lassoRenderContext.data.timeout; @@ -20,12 +37,12 @@ module.exports = function render(input, context) { throw new Error('Lasso page result not found for slot "' + slotName + '". The tag should be used to lasso the page.'); } - lassoRenderContext.emitBeforeSlot(slotName, context); + lassoRenderContext.emitBeforeSlot(slotName, out); if (lassoPageResultAsyncValue.isResolved()) { - renderSlot(slotName, lassoPageResultAsyncValue.data, context, lassoRenderContext); + renderSlot(slotName, lassoPageResultAsyncValue.data, out, lassoRenderContext); } else { - var asyncContext = context.beginAsync({ + var asyncContext = out.beginAsync({ name: 'lasso-slot:' + slotName, timeout: timeout }); diff --git a/test/lasso-test.js b/test/lasso-test.js index 0ee1b5d0..d9c3f968 100644 --- a/test/lasso-test.js +++ b/test/lasso-test.js @@ -1510,4 +1510,34 @@ describe('lasso/index', function() { }) .done(); }); + + it('should allow for CSP nonce on inline script tags', function(done) { + var lasso = require('../'); + var theLasso = lasso.create({ + outputDir: outputDir, + urlPrefix: '/', + fingerprintsEnabled: false, + }, __dirname, __filename); + var writerTracker = require('./WriterTracker').create(theLasso.writer); + theLasso.lassoPage({ + pageName: 'testPage', + dependencies: [ + {path: './src/moduleA/moduleA.js', inline: 'end'}, + './src/moduleB/moduleB.js', + ], + from: module, + basePath: __dirname + }) + .then(function(lassoPageResult) { + var body = lassoPageResult.getSlotHtml('body', { + inlineScriptAttrs: { + nonce: 'abc' + } + }).replace(/\\/g, "/"); + expect(writerTracker.getOutputFilenames()).to.deep.equal(['testPage.js']); + expect(body).to.equal('\n'); + lasso.flushAllCaches(done); + }) + .done(); + }); }); diff --git a/test/taglib-test.js b/test/taglib-test.js index 3166f2d6..79dcaa39 100644 --- a/test/taglib-test.js +++ b/test/taglib-test.js @@ -81,4 +81,24 @@ describe('lasso/taglib' , function() { testRender('test-project/src/pages/page1/template.marko', {}, done); }); + it('should render a page with CSP nonce enabled', function(done) { + require('../').configure({ + outputDir: nodePath.join(__dirname, 'build'), + urlPrefix: '/static', + includeSlotNames: false, + fingerprintsEnabled: true, + flags: ['browser'], + cspNonceProvider: function(out, lassoContext) { + return out.global.cspNonce; + } + }, __dirname); + + + testRender('test-project/src/pages/csp-test/template.marko', { + $global: { + cspNonce: 'abc' + } + }, done); + }); + }); diff --git a/test/test-project/src/pages/csp-test/browser.json b/test/test-project/src/pages/csp-test/browser.json new file mode 100644 index 00000000..9a68697f --- /dev/null +++ b/test/test-project/src/pages/csp-test/browser.json @@ -0,0 +1,8 @@ +{ + "dependencies": [ + "style.css", + "test.js", + { "path": "style-inline.css", "inline": true }, + { "path": "test-inline.js", "inline": true } + ] +} \ No newline at end of file diff --git a/test/test-project/src/pages/csp-test/favicon.ico b/test/test-project/src/pages/csp-test/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b34c9c79ddb5606abc55b74f9941b5d1b47397ef GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%r0{?*$Ll#>YJ^hc2FQqX2N5)L+ zzy3$Y;yc>@BV%N}AU3jI5F1%9h>ffl#2(d8S@;ivw1=KAJ_7}K&;@~f2_Oc^5d!Og z3UaV0$_I*p literal 0 HcmV?d00001 diff --git a/test/test-project/src/pages/csp-test/marko-logo.png b/test/test-project/src/pages/csp-test/marko-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d366c8f8407dddd254c5fc13bc02c6ddab9549b4 GIT binary patch literal 31647 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 + + {@lasso-head /} + + + {@lasso-body /} + + \ No newline at end of file diff --git a/test/test-project/src/pages/csp-test/template.dust.expected.html b/test/test-project/src/pages/csp-test/template.dust.expected.html new file mode 100644 index 00000000..55cd78f6 --- /dev/null +++ b/test/test-project/src/pages/csp-test/template.dust.expected.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/test-project/src/pages/csp-test/template.marko b/test/test-project/src/pages/csp-test/template.marko new file mode 100644 index 00000000..f150b710 --- /dev/null +++ b/test/test-project/src/pages/csp-test/template.marko @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/test-project/src/pages/csp-test/template.marko.expected.html b/test/test-project/src/pages/csp-test/template.marko.expected.html new file mode 100644 index 00000000..fc0f5e09 --- /dev/null +++ b/test/test-project/src/pages/csp-test/template.marko.expected.html @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/test/test-project/src/pages/csp-test/test-inline.js b/test/test-project/src/pages/csp-test/test-inline.js new file mode 100644 index 00000000..7df80eb7 --- /dev/null +++ b/test/test-project/src/pages/csp-test/test-inline.js @@ -0,0 +1 @@ +console.log('hello-inline'); \ No newline at end of file diff --git a/test/test-project/src/pages/csp-test/test.js b/test/test-project/src/pages/csp-test/test.js new file mode 100644 index 00000000..ea17b22e --- /dev/null +++ b/test/test-project/src/pages/csp-test/test.js @@ -0,0 +1 @@ +console.log('hello'); \ No newline at end of file