diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0934fc..073b570 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## v0.1.10
+- **BREAKING CHANGE**, **SECURITY UPDATE**
+ To use `require()` in the template, user will have to pass `allowRequire: true` in option. This option is by default set to false.
+ ```js
+ const newHTMLTemplate = abellRenderer.render(
+ myAbellTemplate,
+ mySandbox,
+ {allowRequire: true}
+ );
+ ```
+
## v0.1.9
- Fix to recursively find and create nested `.abell` files
diff --git a/README.md b/README.md
index d1798ec..6e00b1d 100644
--- a/README.md
+++ b/README.md
@@ -92,7 +92,7 @@ You can use JavaScript Array methods to loop over array. Other JavaScript Array
${user.name}
Age: ${user.age}
- `)
+ `).join('')
}}
@@ -113,6 +113,8 @@ Ouputs:
```
### ⤵️ Import JS/JSON/NPM Modules
+*NOTE: Starting v0.1.10 require() can only be used when `allowRequire: true` is passed from options or `--allow-require` flag is passed in CLI*
+
With Abell you can import your Native NodeJS Modules, NPM Modules, JS Files (should export data), and JSON Files with `require()`
@@ -196,6 +198,7 @@ Outputs:
`template`: Abell template in String
`sandbox`: Object over which the scripts execute, Can define variables and inject them into script.
`options.basePath`: basePath which is prefixed on `require()` paths in abellTemplate.
+`options.allowRequire`: Passing `true` allows using `require()` in templates. Default is `false`.
## 🤗 Contributing
diff --git a/package.json b/package.json
index 7dbc024..fb9b46b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "abell-renderer",
- "version": "0.1.9",
+ "version": "0.1.10",
"description": "A wrapper arround Mustache that adds some additional features and some syntatic sugar required for abell",
"main": "dist/index.js",
"bin": {
@@ -14,8 +14,8 @@
"test": "mocha --recursive \"./tests/*.js\"",
"build": "node build/build.main.js",
"dev": "node src/example/example.js",
- "dev:cli": "node src/bin.js build --input src/example/templates/cli_example.abell --output src/example/out/cli_example.html",
- "dev:cli-dir": "node src/bin.js build --input src/example/templates/loop_example --output src/example/out/loop_example",
+ "dev:cli": "node src/bin.js build --input src/example/templates/cli_example.abell --output src/example/out/cli_example.html --allow-require",
+ "dev:cli-dir": "node src/bin.js build --input src/example/templates/loop_example --output src/example/out/loop_example --allow-require",
"cli": "npm run dev:cli",
"eslint": "eslint .",
"prepublishOnly": "npm run eslint && npm test && npm run build"
diff --git a/src/bin.js b/src/bin.js
index 7106e6d..36bf9de 100755
--- a/src/bin.js
+++ b/src/bin.js
@@ -70,6 +70,7 @@ function build() {
const outputPath = (indexOfOutput > -1)
? path.join(cwd, args[indexOfOutput + 1])
: inputPath.replace('.abell', '.html'); // file name of input
+ const allowRequire = args.includes('--allow-require');
const basePath = path.dirname(inputPath);
@@ -77,7 +78,7 @@ function build() {
if (!fs.statSync(inputPath).isDirectory()) {
// If input is a file
- generateHTMLFromAbell(inputPath, outputPath, {basePath});
+ generateHTMLFromAbell(inputPath, outputPath, {basePath, allowRequire});
} else {
// If input is a directory
const relativePaths = recursiveFind(inputPath, '.abell')
@@ -87,7 +88,7 @@ function build() {
generateHTMLFromAbell(
path.join(inputPath, filepath),
path.join(outputPath, filepath.replace('.abell', '.html')),
- {basePath: path.dirname(path.join(inputPath, filepath))}
+ {basePath: path.dirname(path.join(inputPath, filepath)), allowRequire}
);
}
}
diff --git a/src/index.js b/src/index.js
index e8debc1..9b55fa8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -42,7 +42,7 @@ const execRegexOnAll = (regex, template) => {
* @param {object} options additional options e.g ({basePath})
* @return {string} htmlTemplate
*/
-function render(abellTemplate, sandbox, options = {basePath: ''}) {
+function render(abellTemplate, sandbox, options = {basePath: '', allowRequire: false}) {
// Finds all the JS expressions to be executed.
const {matches, input} = execRegexOnAll(/\\?{{(.+?)}}/gs, abellTemplate);
let renderedHTML = '';
@@ -54,6 +54,9 @@ function render(abellTemplate, sandbox, options = {basePath: ''}) {
// Ignore the match that starts with slash '\' and return the same value without slash
value = match[0].slice(1);
} else if (match[1].includes('require(')) {
+ if (!options.allowRequire) {
+ throw new Error('require() is not allowed in the script');
+ }
// the js block is trying to require (e.g const module1 = require('module1'))
const lines = match[1]
.trim()
diff --git a/tests/execute.spec.js b/tests/execute.spec.js
new file mode 100644
index 0000000..b8e2855
--- /dev/null
+++ b/tests/execute.spec.js
@@ -0,0 +1,45 @@
+const path = require('path');
+const expect = require('chai').expect;
+
+const {
+ execute,
+ executeRequireStatement
+} = require('../src/execute.js');
+
+
+describe('execute() - Executes JavaScript passed to it as string', () => {
+ it('should output added value when addition is performed on two values', () => {
+ expect(
+ execute('24 + 12', {}).value
+ ).to.equal(36);
+ });
+
+ it('should update value of a to new value', () => {
+ expect(
+ execute('a = 22 + 22', {a: 4}).sandbox.a
+ ).to.equal(44);
+ });
+
+ it('should not update value that is inside string', () => {
+ expect(
+ execute('(() => \'a = b\')()').value
+ ).to.equal('a = b');
+ });
+});
+
+
+describe('executeRequireStatement() - executes the code with require() in its string', () => {
+ it('should add path native object when required', () => {
+ expect(
+ executeRequireStatement('const path = require(\'path\')')
+ .path.join('test', 'path')
+ ).to.equal(path.join('test', 'path'));
+ });
+
+ it('should handle the case of require(\'module\').property', () => {
+ expect(
+ executeRequireStatement('const testPath = require(\'path\').join(\'test\',\'path\')')
+ .testPath
+ ).to.equal(path.join('test', 'path'));
+ });
+});
diff --git a/tests/index.spec.js b/tests/index.spec.js
index b029e87..17ca668 100644
--- a/tests/index.spec.js
+++ b/tests/index.spec.js
@@ -2,11 +2,6 @@ const fs = require('fs');
const path = require('path');
const expect = require('chai').expect;
-const {
- execute,
- executeRequireStatement
-} = require('../src/execute.js');
-
const abellRenderer = require('../src/index.js');
describe('render() - renders abellTemplate into HTML Text', () => {
@@ -33,7 +28,10 @@ describe('render() - renders abellTemplate into HTML Text', () => {
.render(
abellTemplate,
sampleSandbox,
- {basePath: path.join(__dirname, 'resources')}
+ {
+ basePath: path.join(__dirname, 'resources'),
+ allowRequire: true
+ }
)
).to.equal(htmlTemplate);
});
@@ -79,11 +77,30 @@ describe('render() - renders abellTemplate into HTML Text', () => {
expect(
abellRenderer.render(
abellTemplate,
- {}
+ {},
+ {allowRequire: true}
).trim()
).to.equal('8 hi/hello hi/hello
');
});
+ it('should throw an error if require() is used without allowRequire: true option', () => {
+ const abellTemplate = `
+ {{
+ const path = require('path');
+ const hiHelloPath = require('path').join('hi', 'hello');
+ }}
+ {{ path.join('hi', 'hello') }} {{ hiHelloPath }}
+ `;
+
+ expect(
+ () =>
+ abellRenderer.render(
+ abellTemplate,
+ {},
+ )
+ ).to.throw('require() is not allowed in the script');
+ });
+
it('should not throw error and return same value if blank brackets passed', () => {
expect(
abellRenderer.render(
@@ -102,41 +119,3 @@ describe('render() - renders abellTemplate into HTML Text', () => {
).to.equal('{{ This is ignored }}');
});
});
-
-
-describe('execute() - Executes JavaScript passed to it as string', () => {
- it('should output added value when addition is performed on two values', () => {
- expect(
- execute('24 + 12', {}).value
- ).to.equal(36);
- });
-
- it('should update value of a to new value', () => {
- expect(
- execute('a = 22 + 22', {a: 4}).sandbox.a
- ).to.equal(44);
- });
-
- it('should not update value that is inside string', () => {
- expect(
- execute('(() => \'a = b\')()').value
- ).to.equal('a = b');
- });
-});
-
-
-describe('executeRequireStatement() - executes the code with require() in its string', () => {
- it('should add path native object when required', () => {
- expect(
- executeRequireStatement('const path = require(\'path\')')
- .path.join('test', 'path')
- ).to.equal(path.join('test', 'path'));
- });
-
- it('should handle the case of require(\'module\').property', () => {
- expect(
- executeRequireStatement('const testPath = require(\'path\').join(\'test\',\'path\')')
- .testPath
- ).to.equal(path.join('test', 'path'));
- });
-});