Skip to content
This repository has been archived by the owner on Sep 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #8 from jonathanKingston/caching-writer
Browse files Browse the repository at this point in the history
Caching writer
  • Loading branch information
rwjblue committed Nov 17, 2015
2 parents b4bca03 + cd4ec03 commit dd852dd
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 71 deletions.
1 change: 0 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"extends": "nightmare-mode",
"env": {
"node": true
},
Expand Down
7 changes: 7 additions & 0 deletions .snyk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ignore:
'npm:uglify-js:20150824':
- 'broccoli@0.16.8 > handlebars@3.0.3 > uglify-js@2.3.6':
reason: Waiting for update of ember-cli
expires: '2015-12-03T20:02:27.923Z'
patch: {}
version: v1
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ language: node_js
node_js:
- "0.10"
- "0.12"
- "iojs"
- "4"
- "5"

sudo: false

Expand All @@ -13,7 +14,7 @@ cache:

before_install:
- "npm config set spin false"
- "npm install -g npm@^2"
- "npm install -g npm@^3"

install:
- npm install
Expand Down
9 changes: 0 additions & 9 deletions Brocfile.js

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This plugin looks at an apps html files to rewrite their content with integrity
- **paranoiaCheck** - true by default, this turns off the integrity attribute if any Unicode is found within the file.

### Example
```
```js
var sriTree = sri('path/to/code, {
prefix: 'https://example.com/',
crossorigin: 'anonymous'
Expand Down
115 changes: 74 additions & 41 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
var Filter = require('broccoli-filter');
var CachingWriter = require('broccoli-caching-writer');
var sriToolbox = require('sri-toolbox');
var fs = require('fs');
var crypto = require('crypto');
var styleCheck = /\srel=["\'][^"]*stylesheet[^"]*["\']/;
var srcCheck = /\ssrc=["\']([^"\']+)["\']/;
var hrefCheck = /\shref=["\']([^"\']+)["\']/;
var symlinkOrCopy = require('symlink-or-copy').sync;
var Promise = require('rsvp').Promise; // node 0.10

function SRIHashAssets(inputNode, options) {
var path = require('path');

var STYLE_CHECK = /\srel=["\'][^"]*stylesheet[^"]*["\']/;
var SRC_CHECK = /\ssrc=["\']([^"\']+)["\']/;
var HREF_CHECK = /\shref=["\']([^"\']+)["\']/;
var SCRIPT_CHECK = new RegExp('<script[^>]*src=["\']([^"]*)["\'][^>]*>', 'g');
var LINT_CHECK = new RegExp('<link[^>]*href=["\']([^"]*)["\'][^>]*>', 'g');
var INTEGRITY_CHECK = new RegExp('integrity=["\']');
var CROSS_ORIGIN_CHECK = new RegExp('crossorigin=["\']([^"\']+)["\']');
var MD5_CHECK = /^(.*)[-]([a-z0-9]{32})([.].*)$/;

function SRIHashAssets(inputNodes, options) {
if (!(this instanceof SRIHashAssets)) {
return new SRIHashAssets(inputNode, options);
return new SRIHashAssets(inputNodes, options);
}

this.options = options || {};
this.context = this.options.context || {};
Filter.call(this, inputNode);
var nodes = inputNodes;
if (!Array.isArray(nodes)) {
nodes = [nodes];
}

CachingWriter.call(this, nodes, {
// disabled to ensure all files are synced forward
// I suspect additions to BCW are needed, or a slightly different plugin
// to handle this more elegantly.
// Leaving this comment here as a reminder. -sp
//
// cacheInclude: [
// /\.html$/,
// /\.js$/,
// /\.css$/
// ]
});

this.options.paranoiaCheck = this.options.paranoiaCheck || true;

Expand All @@ -27,19 +51,14 @@ function SRIHashAssets(inputNode, options) {
}
}

SRIHashAssets.prototype = Object.create(Filter.prototype);
SRIHashAssets.prototype = Object.create(CachingWriter.prototype);
SRIHashAssets.prototype.constructor = SRIHashAssets;

SRIHashAssets.prototype.extensions = ['html'];
SRIHashAssets.prototype.targetExtension = 'html';
SRIHashAssets.prototype.addSRI = function addSRI(string, srcDir) {
var plugin = this;

SRIHashAssets.prototype.addSRI = function addSRI(string, file) {
var that = this;
var scriptCheck = new RegExp('<script[^>]*src=["\']([^"]*)["\'][^>]*>', 'g');
var linkCheck = new RegExp('<link[^>]*href=["\']([^"]*)["\'][^>]*>', 'g');

return string.replace(scriptCheck, function srcMatch(match) {
var src = match.match(srcCheck);
return string.replace(SCRIPT_CHECK, function srcMatch(match) {
var src = match.match(SRC_CHECK);
var filePath;

if (!src) {
Expand All @@ -48,20 +67,19 @@ SRIHashAssets.prototype.addSRI = function addSRI(string, file) {

filePath = src[1];

return that.mungeOutput(match, filePath, file);
}).replace(linkCheck, function hrefMatch(match) {
var href = match.match(hrefCheck);
var isStyle = styleCheck.test(match);
return plugin.mungeOutput(match, filePath, srcDir);
}).replace(LINT_CHECK, function hrefMatch(match) {
var href = match.match(HREF_CHECK);
var isStyle = STYLE_CHECK.test(match);
var filePath;


if (!isStyle || !href) {
return match;
}

filePath = href[1];

return that.mungeOutput(match, filePath, file);
return plugin.mungeOutput(match, filePath, srcDir);
});
};

Expand All @@ -73,6 +91,7 @@ SRIHashAssets.prototype.readFile = function readFile(dirname, file) {
} catch(e) {
return null;
}

return assetSource;
};

Expand Down Expand Up @@ -102,7 +121,6 @@ SRIHashAssets.prototype.paranoiaCheck = function paranoiaCheck(assetSource) {
};

SRIHashAssets.prototype.generateIntegrity = function generateIntegrity(output, file, dirname, external) {
var crossoriginCheck = new RegExp('crossorigin=["\']([^"\']+)["\']');
var assetSource = this.readFile(dirname, file);
var selfCloseCheck = /\s*\/>$/;
var integrity;
Expand All @@ -125,7 +143,7 @@ SRIHashAssets.prototype.generateIntegrity = function generateIntegrity(output, f
append = ' integrity="' + integrity + '"';

if (external && this.options.crossorigin) {
if (!crossoriginCheck.test(output)) {
if (!CROSS_ORIGIN_CHECK.test(output)) {
append = append + ' crossorigin="' + this.options.crossorigin + '" ';
}
}
Expand All @@ -139,8 +157,7 @@ SRIHashAssets.prototype.generateIntegrity = function generateIntegrity(output, f
};

SRIHashAssets.prototype.checkExternal = function checkExternal(output, file, dirname) {
var md5Check = /^(.*)[-]([a-z0-9]{32})([.].*)$/;
var md5Matches = file.match(md5Check);
var md5Matches = file.match(MD5_CHECK);
var md5sum = crypto.createHash('md5');
var assetSource;
var filePath;
Expand All @@ -163,38 +180,54 @@ SRIHashAssets.prototype.checkExternal = function checkExternal(output, file, dir
return output;
}
}

md5sum.update(assetSource);
if (md5Matches[2] === md5sum.digest('hex')) {
return this.generateIntegrity(output, filePath, dirname, true);
}
return output;
};

SRIHashAssets.prototype.mungeOutput = function mungeOutput(output, filePath, file) {
var integrityCheck = new RegExp('integrity=["\']');
SRIHashAssets.prototype.mungeOutput = function mungeOutput(output, filePath, srcDir) {
var newOutput = output;

if (/^https?:\/\//.test(filePath)) {
return this.checkExternal(output, filePath, file);
return this.checkExternal(output, filePath, srcDir);
}
if (!(integrityCheck.test(output))) {
newOutput = this.generateIntegrity(output, filePath, file);

if (!INTEGRITY_CHECK.test(output)) {
newOutput = this.generateIntegrity(output, filePath, srcDir);
}
return newOutput;
};

SRIHashAssets.prototype.processFile = function processFile(srcDir, destDir, relativePath) {
var fileContent = fs.readFileSync(srcDir + '/' + relativePath);
var that = this;
SRIHashAssets.prototype.processHTMLFile = function processFile(entry) {
var srcDir = path.dirname(entry.fullPath);
var fileContent = this.addSRI(fs.readFileSync(entry.fullPath,'UTF-8'), srcDir);

this._srcDir = srcDir;
fileContent = this.addSRI(fileContent.toString(), srcDir);
fs.writeFileSync(this.outputPath + '/' + entry.relativePath, fileContent);
};

return Promise.resolve().then(function writeFileOutput() {
var outputPath = that.getDestFilePath(relativePath);
SRIHashAssets.prototype.processOtherFile = function(entry) {
symlinkOrCopy(entry.fullPath, this.outputPath + '/' + entry.relativePath);
};

fs.writeFileSync(destDir + '/' + outputPath, fileContent);
SRIHashAssets.prototype.build = function () {
var html = [];
var other = [];

this.listEntries().forEach(function(entry) {
if (/\.html$/.test(entry.relativePath)) {
html.push(entry);
} else {
other.push(entry);
}
});

return Promise.all([
Promise.all(html.map(this.processHTMLFile.bind(this))),
Promise.all(other.map(this.processOtherFile.bind(this)))
]);
};

module.exports = SRIHashAssets;
24 changes: 14 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"lint": "eslint index.js",
"test": "rm -rf tmp && mkdir tmp && npm run lint > tmp/lint-out && broccoli build tmp/output && mocha"
"test": "mocha test",
"test:debug": "mocha debug test"
},
"author": "Jonathan Kingston",
"repository": {
Expand All @@ -14,16 +15,19 @@
},
"license": "MIT",
"dependencies": {
"broccoli": "^0.16.3",
"broccoli-filter": "^1.1.0",
"rsvp": "^3.0.0",
"sri-toolbox": "^0.2.0"
"broccoli-caching-writer": "^2.2.0",
"rsvp": "^3.1.0",
"snyk": "^1.1.0",
"sri-toolbox": "^0.2.0",
"symlink-or-copy": "^1.0.1"
},
"devDependencies": {
"broccoli-cli": "^1.0.0",
"chai": "^3.0.0",
"eslint": "^1.0.0",
"eslint-config-nightmare-mode": "0.1.0",
"mocha": "^2.2.5"
"broccoli": "^0.16.8",
"chai": "^3.4.1",
"eslint": "^1.9.0",
"eslint-config-nightmare-mode": "0.3.0",
"mocha": "^2.3.4",
"mocha-eslint": "^1.0.0",
"walk-sync": "^0.2.6"
}
}
Empty file added test/fixtures/input/omg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions test/fixtures/output2/test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!-- styles -->
<link rel="stylesheet" href="https://example.com/thing.css">
<link rel="stylesheet" href="https://example.com/thing.css">
<link rel="stylesheet" href="vendor.css" />
<link rel="stylesheet" href="other.css" integrity="sha256-ZRXDQAaaiAOOtUbmDL5Ty2JR8Zf1AonXn6DCmchNhvk= sha512-dtOFpumNJPKES8S72UuTAXS6daEMYQKpRpCzlFAto/DHdx7fH0KK8sO9LjLpOjVWrDXE4G+MtisGdWa19wieSg==" />
<link rel="stylesheet" hresf="other.css" />
<link href="other.css" />
<linkhref="other.css" rel="stylesheet" />
<link h="other.css" rel="stylesheet" />
<link hsref="other.css" rel="stylesheet" />
<link href="other.css" rel="stylesheet" integrity="sha256-ZRXDQAaaiAOOtUbmDL5Ty2JR8Zf1AonXn6DCmchNhvk= sha512-dtOFpumNJPKES8S72UuTAXS6daEMYQKpRpCzlFAto/DHdx7fH0KK8sO9LjLpOjVWrDXE4G+MtisGdWa19wieSg==" />
<link href="other.css" rel="stylesheet" integrity="sha256-ZRXDQAaaiAOOtUbmDL5Ty2JR8Zf1AonXn6DCmchNhvk= sha512-dtOFpumNJPKES8S72UuTAXS6daEMYQKpRpCzlFAto/DHdx7fH0KK8sO9LjLpOjVWrDXE4G+MtisGdWa19wieSg==" >
<link href="other.css" rel="stylesheet" integrity="sha256-ZRXDQAaaiAOOtUbmDL5Ty2JR8Zf1AonXn6DCmchNhvk= sha512-dtOFpumNJPKES8S72UuTAXS6daEMYQKpRpCzlFAto/DHdx7fH0KK8sO9LjLpOjVWrDXE4G+MtisGdWa19wieSg==" >
<link rel="stylesheet" href="https://example.com/vendor.css" />
<link rel="stylesheet" href="https://subdomain.cloudfront.net/assets/vendor-d41d8cd98f00b204e9800998ecf8427e.css">

<!-- scripts -->
<script src="thing.js" integrity="sha256-oFeuE/P+XJMjkMS5pAPudQOMGJQ323nQt+DQ+9zbdAg= sha512-+EXjzt0I7g6BjvqqjkkboGyRlFSfIuyzY2SQ43HQKZBrHsjmRzEdjSHhiDzVs30nXL9H0tKw6WbMPc6RfzUumQ==" ></script>
<script src="moment-with-locales.min.js"></script>
<script src="unicode-chars.js"></script>
<script srsc="unicode-chars.js"></script>
<script s="unicode-chars.js"></script>
<script src="https://example.com/thing-5e1978f9cfa158d9841d7b6d8a4e5c57.js" integrity="sha256-oFeuE/P+XJMjkMS5pAPudQOMGJQ323nQt+DQ+9zbdAg= sha512-+EXjzt0I7g6BjvqqjkkboGyRlFSfIuyzY2SQ43HQKZBrHsjmRzEdjSHhiDzVs30nXL9H0tKw6WbMPc6RfzUumQ==" crossorigin="anonymous" ></script>
<script src="https://example.com/thing-5e1978f9cfa158d9841d7b6d8a4e5c57.js" integrity="sha256-oFeuE/P+XJMjkMS5pAPudQOMGJQ323nQt+DQ+9zbdAg= sha512-+EXjzt0I7g6BjvqqjkkboGyRlFSfIuyzY2SQ43HQKZBrHsjmRzEdjSHhiDzVs30nXL9H0tKw6WbMPc6RfzUumQ==" crossorigin="anonymous" ></script>
<script src="https://example.com/thing-5e1978f9cfa158d9841d7b6d8a4e5c57.js" crossorigin="use-credentials" integrity="sha256-oFeuE/P+XJMjkMS5pAPudQOMGJQ323nQt+DQ+9zbdAg= sha512-+EXjzt0I7g6BjvqqjkkboGyRlFSfIuyzY2SQ43HQKZBrHsjmRzEdjSHhiDzVs30nXL9H0tKw6WbMPc6RfzUumQ==" ></script>
<script src="https://example.com/thing"></script>
<script src="https://example.com/thing" integrity="thing"></script>

<!-- other -->
<link rel="icon" type="image/png" href="favicon.png" sizes="16x16" />
1 change: 1 addition & 0 deletions test/fixtures/output2/thing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('test');
1 change: 1 addition & 0 deletions test/fixtures/output2/unicode-chars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('I ♡ WebAppSec!');
Loading

0 comments on commit dd852dd

Please sign in to comment.