SimpliField/jsub

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# jsub
> JavaScript library to create JavaScript subsets.

[![NPM version](https://badge.fury.io/js/jsub.svg)](https://npmjs.org/package/jsub)
[![Build status](https://secure.travis-ci.org/SimpliField/jsub.svg)](https://travis-ci.org/SimpliField/jsub)
[![Dependency Status](https://david-dm.org/SimpliField/jsub.svg)](https://david-dm.org/SimpliField/jsub)
[![devDependency Status](https://david-dm.org/SimpliField/jsub/dev-status.svg)](https://david-dm.org/SimpliField/jsub#info=devDependencies)
[![Coverage Status](https://coveralls.io/repos/SimpliField/jsub/badge.svg?branch=master)](https://coveralls.io/r/SimpliField/jsub?branch=master)
[![Code Climate](https://codeclimate.com/github/SimpliField/jsub.svg)](https://codeclimate.com/github/SimpliField/jsub)

## Usage

Simply require `jsub` and test your scripts against your own JavaScript subset
 syntax definition. Scripts must be provided as
 [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) with tools like
 Esprima or the [Reflect.parse](https://github.com/estree/estree) API:

```js
var jsub = require('jsub');
var esprima = require('esprima');

var syntax = {
  context: {
    categories: {
      fruits: [],
      vegetables: [],
    },
  },
  conditions: [{
    type: 'Program',
  }, {
    type: 'ExpressionStatement',
  }, {
    type: 'BinaryExpression',
    operator: ['*', '-'],
  }, {
    type: 'Literal',
    raw: /^[0-9]{1,5}$/,
  }, {
    type: 'CallExpression',
    // The $_ property creates a custom parser that will by-pass the actual
    // jsub embedded child expression checker. You should use this extremely
    // carefully and heavily test it. It must return an array of errors that
    // prevented the rule to apply, empty if it successfully applied.
    '$_': function(expression) {
      // Check function name
      if(
        (!expression.callee) ||
        'Identifier' !== expression.callee.type ||
        'lengthOf' !== expression.callee.name
      ) {
        return [new Error('E_BAD_FUNCTION_NAME')];
      }
      if(
        1 !== expression.arguments.length ||
        'Literal' !== expression.arguments[0].type ||
        !/^fruits|vegetables$/.test(expression.arguments[0].value)
      ) {
        return [new Error('E_BAD_FUNCTION_ARGS')];
      }
      return [];
    },
  }, {
    type: 'CallExpression',
    // The $_ property is also usefull to check sub syntaxes
    '$_': function(expression) {
      return jsub.bind(null, {
        type: 'Literal',
        raw: /^[0-9]{1,5}$/,
      });
    },
  }],
};

var checkJavaScriptSubset = jsub.bind(null, syntax);

var script = '2 * (lengthOf("fruits") - lengthOf("vegetables"))';

var javaScriptAST = esprima.parse(script);

checkJavaScriptSubset(javaScriptAST);
// []
// returns an empty array since there is no syntax violation

```

`jsub` uses a white list to check every AST node of your application so it
  fallbacks to security, you can now run your script safely without having to
  sandbox it!

```js
var script = '2 * (lengthOf("fruits") - lengthOf("vegetables"))';

var context = {
  vegetables: ['salad', 'potato'],
  fruits: ['cherry'],
  lengthOf: function(arrayName) {
    return context[arrayName].length;
  },
};

runFunction = new Function(
  'var fruits = this.fruits;\n' +
  'var vegetables = this.vegetables;\n' +
  'var lengthOf = this.lengthOf;\n' +
  'return (' + script + ');'
);

console.log(runFunction.call(context));
// -2
```

## API

### jsub(syntax:Object, ast:Object):Array

Check the given `ast` script according to the `syntax` definition, returns an
 array containing the script syntax violations according to the definition.

## Stats
[![NPM](https://nodei.co/npm/jsub.png?downloads=true&stars=true)](https://nodei.co/npm/jsub/)
[![NPM](https://nodei.co/npm-dl/jsub.png)](https://nodei.co/npm/jsub/)