Skip to content

Commit

Permalink
feat: add dangerous but flexible @eval to preprocess
Browse files Browse the repository at this point in the history
  • Loading branch information
3cp committed May 24, 2019
1 parent 37232ba commit d49eb7a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
18 changes: 18 additions & 0 deletions lib/preprocess/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ function replace(rv, rule, cb) {
return rule.reduce((rv, rule) => rv.replace(rule, cb), rv);
}

function dangerousEval(expression, properties) {
const wrapped = `with (properties) { return (${expression}); }`;

try {
// eslint-disable-next-line no-new-func
const result = (new Function('properties', wrapped))(properties);
if (result && result.toString) {
return result.toString();
}
return '';
} catch (e) {
throw new Error(`Eval expression: ${expression} error:${e.message}`);
}
}

// properties is like {name: 'app-name', foo: 'lorem'}
// features is like ['aurelia', 'typescript', 'jest']
module.exports = function(source, properties, features, mode) {
Expand All @@ -65,5 +80,8 @@ module.exports = function(source, properties, features, mode) {
);

rv = replace(rv, rules.echo, (match, variable) => properties[variable] || '');

rv = replace(rv, rules.eval, (match, expression) => dangerousEval(expression, properties));

return rv;
};
5 changes: 5 additions & 0 deletions lib/preprocess/regex-rules.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
exports.html = {
echo: '<!--[ \t]*@echo[ \t]+(.*?)[ \t]*(?:-->|!>)',
eval: '<!--[ \t]*@eval[ \t]+(.*?)[ \t]*(?:-->|!>)',
if: {
start: '[ \t]*<!--[ \t]*@if[ \t]+(.*?)[ \t]*(?:-->|!>)(?:[ \t]*\n+)?',
end : '[ \t]*<!(?:--)?[ \t]*@endif[ \t]*(?:-->|!>)(?:[ \t]*\n)?'
Expand All @@ -11,6 +12,10 @@ exports.js = {
'/\\*[ \t]*@echo[ \t]+(.*?)[ \t]*\\*(?:\\*|/)',
'//[ \t]*@echo[ \t]+(.*?)[ \t]*$'
],
eval: [
'/\\*[ \t]*@eval[ \t]+(.*?)[ \t]*\\*(?:\\*|/)',
'//[ \t]*@eval[ \t]+(.*?)[ \t]*$'
],
if: {
start: '[ \t]*(?://|/\\*)[ \t]*@if[ \t]+([^\n*]*)(?:\\*(?:\\*|/))?(?:[ \t]*\n+)?',
end : '[ \t]*(?://|/\\*)[ \t]*@endif[ \t]*(?:\\*(?:\\*|/))?(?:[ \t]*\n)?'
Expand Down
84 changes: 84 additions & 0 deletions test/preprocess/eval.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import test from 'ava';
import preprocess from '../../lib/preprocess';

test('@eval resolves and evals variable in html syntax', t => {
t.is(
preprocess('a<!-- @eval FINGERPRINT -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEFc'
);
t.is(
preprocess('a<!-- @eval FINGERPRINT.toLowerCase() -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xdeadbeefc'
);
});

test('@eval resolves and evals variable (block comment) in js syntax', t => {
t.is(
preprocess('a/* @eval FINGERPRINT */c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xDEADBEEFc'
);
t.is(
preprocess('a/* @eval JSON.stringify(FINGERPRINT) */c', {FINGERPRINT: '0xDEADBEEF'}),
'a"0xDEADBEEF"c'
);
});

test('@eval resolves and evals variable (line comment) in js syntax', t => {
t.is(
preprocess('a\n// @eval FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF\nc'
);
t.is(
preprocess('a\n// @eval FINGERPRINT.slice(2, 4)\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\nDE\nc'
);
});

test('multiple @eval without overreaching in js syntax', t => {
t.is(
preprocess('a/* @eval FOO */b/* @eval BAR */c', {FOO: 1, BAR: 2}),
'a1b2c'
);
t.is(
preprocess('a/* @eval FOO + 10 */b/* @eval BAR * 2 */c', {FOO: 1, BAR: 2}),
'a11b4c'
);
});

test('@eval allows ommitting of whitespaces before and after @eval in html syntax', t => {
t.is(
preprocess('a<!--@eval FINGERPRINT-->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEFc'
);
t.is(
preprocess('a<!--@eval FINGERPRINT + \'#\' -->c', {FINGERPRINT: '0xDEADBEEF'}, null, 'html'),
'a0xDEADBEEF#c'
);
});

test('@eval allows ommitting of whitespaces before and after @eval (block comment) in js syntax', t => {
t.is(
preprocess('a/*@eval FINGERPRINT*/c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xDEADBEEFc'
);
t.is(
preprocess('a/*@eval FINGERPRINT.toLowerCase()*/c', {FINGERPRINT: '0xDEADBEEF'}),
'a0xdeadbeefc'
);
});

test('@eval allows ommitting of whitespaces before and after @eval (line comment) in js syntax', t => {
t.is(
preprocess('a\n//@eval FINGERPRINT\nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF\nc'
);
t.is(
preprocess('a\n//@eval FINGERPRINT + "##" \nc', {FINGERPRINT: '0xDEADBEEF'}),
'a\n0xDEADBEEF##\nc'
);
});


test('@eval complains about broken js syntax', t => {
t.throws(() => preprocess('a\n//@eval FINGERPRINT+\nc', {FINGERPRINT: '0xDEADBEEF'}));
});

0 comments on commit d49eb7a

Please sign in to comment.