This commit is contained in:
Nils Knappmeier
2020-05-31 23:38:23 +02:00
parent 77825f8d35
commit 55f65eaa2e
14 changed files with 1106 additions and 668 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ module.exports = {
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 6,
ecmaVersion: 2018,
ecmaFeatures: {}
}
};
+27 -4
View File
@@ -165,6 +165,29 @@ module.exports = function(grunt) {
}
}
},
karma: {
options: {
configFile: 'karma.conf.js'
},
saucelabs: {
build: process.env.TRAVIS_JOB_ID || 'non-travis',
configFile: 'karma-setup/default/karma.conf.js',
...require('./karma-setup/shared/saucelabs'),
singleRun: true
},
default: {
browsers: [],
configFile: 'karma-setup/default/karma.conf.js',
singleRun: false,
autoWatch: true
},
amd: {
configFile: 'karma-setup/requirejs/karma.conf.js',
browsers: [],
singleRun: false,
autoWatch: true
}
},
'saucelabs-mocha': {
all: {
options: {
@@ -178,8 +201,8 @@ module.exports = function(grunt) {
browsers: [
{ browserName: 'chrome' },
{ browserName: 'firefox', platform: 'Linux' },
// {browserName: 'safari', version: 9, platform: 'OS X 10.11'},
// {browserName: 'safari', version: 8, platform: 'OS X 10.10'},
{ browserName: 'safari', version: 9, platform: 'OS X 10.11' },
{ browserName: 'safari', version: 8, platform: 'OS X 10.10' },
{
browserName: 'internet explorer',
version: 11,
@@ -238,7 +261,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-bg-shell');
grunt.loadNpmTasks('@knappi/grunt-saucelabs');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-webpack');
grunt.task.loadTasks('tasks');
@@ -268,7 +291,7 @@ module.exports = function(grunt) {
grunt.registerTask('bench', ['metrics']);
if (process.env.SAUCE_ACCESS_KEY) {
grunt.registerTask('sauce', ['concat:tests', 'connect', 'saucelabs-mocha']);
grunt.registerTask('sauce', ['karma:saucelabs']);
} else {
grunt.registerTask('sauce', []);
}
+8
View File
@@ -0,0 +1,8 @@
const files = require('../shared/default-files');
module.exports = function(config) {
config.set({
...require('../shared/default-config'),
files: [...files('dist/handlebars.js'), 'spec/*.js']
});
};
+17
View File
@@ -0,0 +1,17 @@
module.exports = function(config) {
config.set({
...require('../shared/default-config'),
...require('../shared/saucelabs'),
files: [
'../../node_modules/sinon/pkg/sinon.js',
'../../node_modules/chai/chai.js',
'../../node_modules/dirty-chai/lib/dirty-chai.js',
'../../node_modules/mocha/mocha.js',
'../../spec/karma-includes/browser-context.js',
'../../spec/karma-includes/mocha.js',
'../../spec/vendor/json2.js',
'../../spec/env/common.js',
{ pattern: '../../spec/*.js', included: false }
]
});
};
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
frameworks: ['mocha'],
// concurrency: we have no concurrency set, because this seems to let Karma hang after the first batch
// of browsers have finished their tests.
reporters: ['dots']
};
+13
View File
@@ -0,0 +1,13 @@
module.exports = function files(...additionalFiles) {
return [
'node_modules/sinon/pkg/sinon.js',
'node_modules/chai/chai.js',
'node_modules/dirty-chai/lib/dirty-chai.js',
'node_modules/mocha/mocha.js',
...additionalFiles,
'spec/browser-setup/browser-context.js',
'spec/browser-setup/mocha.js',
'spec/vendor/json2.js',
'spec/env/common.js'
];
};
+47
View File
@@ -0,0 +1,47 @@
// Example set of browsers to run on Sauce Labs
// Check out https://saucelabs.com/platforms for all browser/platform combos
const customLaunchers = {
sl_chrome: {
base: 'SauceLabs',
browserName: 'chrome'
},
sl_firefox: {
base: 'SauceLabs',
browserName: 'firefox',
platform: 'Linux'
},
sl_safari_9: {
base: 'SauceLabs',
browserName: 'safari',
version: 9,
platform: 'OS X 10.11'
},
sl_safari_8: {
base: 'SauceLabs',
browserName: 'safari',
version: 8,
platform: 'OS X 10.10'
},
sl_ie_10: {
base: 'SauceLabs',
browserName: 'internet explorer',
version: 10,
platform: 'Windows 8'
},
sl_ie_11: {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 8.1',
version: '11'
}
};
const browsers = Object.keys(customLaunchers);
module.exports = {
customLaunchers,
browsers,
sauceLabs: {
testName: 'handlebars-unit-tests'
}
};
+905 -626
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -58,9 +58,13 @@
"grunt-contrib-requirejs": "^1",
"grunt-contrib-uglify": "^1",
"grunt-contrib-watch": "^1.1.0",
"grunt-karma": "^4.0.0",
"grunt-webpack": "^1.0.8",
"husky": "^3.1.0",
"jison": "~0.3.0",
"karma": "^5.0.5",
"karma-mocha": "^2.0.1",
"karma-sauce-launcher": "^4.1.4",
"lint-staged": "^9.5.0",
"mocha": "^5",
"mock-stdin": "^0.3.0",
+1 -2
View File
@@ -112,8 +112,7 @@
failedTests.push({
name: test.title,
result: false,
message: err.message,
stack: err.stack,
message: err.message, stack: err.stack,
titles: flattenTitles(test)
});
}
+22
View File
@@ -0,0 +1,22 @@
window.CompilerContext = {
compile: function(template, options) {
var templateSpec = handlebarsEnv.precompile(template, options);
return handlebarsEnv.template(safeEval(templateSpec));
},
compileWithPartial: function(template, options) {
return handlebarsEnv.compile(template, options);
}
};
function safeEval(templateSpec) {
try {
var ret;
// eslint-disable-next-line no-eval
eval('ret = ' + templateSpec);
return ret;
} catch (err) {
// eslint-disable-next-line no-console
console.error(templateSpec);
throw err;
}
}
+2
View File
@@ -0,0 +1,2 @@
window.expect = chai.expect;
mocha.setup('bdd');
+21
View File
@@ -0,0 +1,21 @@
/* global requirejs */
var tests = [];
requirejs.config({
// Karma serves files from '/base'
baseUrl: '/base/src',
paths: {
handlebars: '/dist/handlebars.amd',
tests: '/tmp/tests'
},
shim: {},
// ask Require.js to load these files (all our tests)
deps: ['handlebars', tests],
// start test run, once Require.js is done
callback: window.__karma__.start
});
+32 -35
View File
@@ -1,4 +1,15 @@
describe('security issues', function() {
var consoleErrorFake = null;
beforeEach(function() {
consoleErrorFake = sinon.fake();
sinon.replace(console, 'error', consoleErrorFake);
});
afterEach(function() {
sinon.restore();
});
describe('GH-1495: Prevent Remote Code Execution via constructor', function() {
it('should not allow constructors to be accessed', function() {
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
@@ -88,17 +99,15 @@ describe('security issues', function() {
});
it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
var functionCalls = [];
var fakeFunction = sinon.fake();
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
template(
{
fn: function() {
functionCalls.push('called');
}
fn: fakeFunction
},
{ allowCallsToHelperMissing: true }
);
equals(functionCalls.length, 1);
expect(fakeFunction.calledOnce).to.be.true();
});
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
@@ -185,20 +194,18 @@ describe('security issues', function() {
function checkProtoMethodAccess(compileOptions) {
it('should be prohibited by default and log a warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aMethod}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
.toCompileTo('');
expect(spy.calledOnce).to.be.true();
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
expect(consoleErrorFake.calledOnce).to.be.true();
expect(consoleErrorFake.args[0][0]).to.match(
/Handlebars: Access has been denied/
);
});
it('should only log the warning once', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aMethod}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -209,13 +216,13 @@ describe('security issues', function() {
.withCompileOptions(compileOptions)
.toCompileTo('');
expect(spy.calledOnce).to.be.true();
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
expect(consoleErrorFake.calledOnce).to.be.true();
expect(consoleErrorFake.args[0][0]).to.match(
/Handlebars: Access has been denied/
);
});
it('can be allowed, which disables the warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aMethod}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -226,12 +233,10 @@ describe('security issues', function() {
})
.toCompileTo('returnValue');
expect(spy.callCount).to.equal(0);
expect(consoleErrorFake.callCount).to.equal(0);
});
it('can be turned on by default, which disables the warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aMethod}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -240,12 +245,10 @@ describe('security issues', function() {
})
.toCompileTo('returnValue');
expect(spy.callCount).to.equal(0);
expect(consoleErrorFake.callCount).to.equal(0);
});
it('can be turned off by default, which disables the warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aMethod}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -254,7 +257,7 @@ describe('security issues', function() {
})
.toCompileTo('');
expect(spy.callCount).to.equal(0);
expect(consoleErrorFake.callCount).to.equal(0);
});
it('can be turned off, if turned on by default', function() {
@@ -300,20 +303,18 @@ describe('security issues', function() {
function checkProtoPropertyAccess(compileOptions) {
it('should be prohibited by default and log a warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aProperty}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
.toCompileTo('');
expect(spy.calledOnce).to.be.true();
expect(spy.args[0][0]).to.match(/Handlebars: Access has been denied/);
expect(consoleErrorFake.calledOnce).to.be.true();
expect(consoleErrorFake.args[0][0]).to.match(
/Handlebars: Access has been denied/
);
});
it('can be explicitly prohibited by default, which disables the warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aProperty}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -322,12 +323,10 @@ describe('security issues', function() {
})
.toCompileTo('');
expect(spy.callCount).to.equal(0);
expect(consoleErrorFake.callCount).to.equal(0);
});
it('can be turned on, which disables the warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aProperty}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -338,12 +337,10 @@ describe('security issues', function() {
})
.toCompileTo('propertyValue');
expect(spy.callCount).to.equal(0);
expect(consoleErrorFake.callCount).to.equal(0);
});
it('can be turned on by default, which disables the warning', function() {
var spy = sinon.spy(console, 'error');
expectTemplate('{{aProperty}}')
.withInput(new TestClass())
.withCompileOptions(compileOptions)
@@ -352,7 +349,7 @@ describe('security issues', function() {
})
.toCompileTo('propertyValue');
expect(spy.callCount).to.equal(0);
expect(consoleErrorFake.callCount).to.equal(0);
});
it('can be turned off, if turned on by default', function() {