All files SimpleRule.js

88.57% Statements 31/35
84.85% Branches 28/33
100% Functions 6/6
88.57% Lines 31/35
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 1031x   1x                       1x             1x         40x   40x 1x       39x 2x         37x 2x 1x               48x 8x 8x   40x 40x 38x 115x   35x             9x 9x 7x 2x         5x     6x               17x         17x         17x 9x       1x      
const esprima = require("esprima");
 
const permittedExpressionTypes = [
    "ExpressionStatement",
    "LogicalExpression",
    "Literal",
    "MemberExpression",
    "BinaryExpression",
    "CallExpression",
    "Identifier",
    "ArrowFunctionExpression",
    "UnaryExpression"
];
 
const permittedMemberFunctionCalls = [
    "filter",
    "map",
    "reduce",
    "hasOwnProperty"
];
 
const permittedUnaryFunctionCalls = [
    "!"
];
 
function validateSyntaxNode( node) {
    Iif (! node.type)
        return;
    if (!permittedExpressionTypes.includes(node.type)) {
        throw {
            error: "invalid_policy",
            message: `${node.type}s are not allowed in 'condition'.`
        };
    } else if (node.type === "CallExpression") {
        Iif (!permittedMemberFunctionCalls.includes(node.callee.property.name))
            throw {
                error: "invalid_policy",
                message: `Calling ${node.callee.property.name} is not allowed in 'condition'.`
            };
    } else if (node.type === "UnaryExpression") {
        if (!permittedUnaryFunctionCalls.includes(node.operator))
            throw {
                error: "invalid_policy",
                message: `Calling ${node.operator} is not allowed in 'condition'.`
            };
    }
}
 
function checkSyntaxTreeNodeTypes(node) {
    if (node instanceof Array) {
        node.forEach(element => {
            checkSyntaxTreeNodeTypes(element);
        });
    } else Eif (node instanceof Object) {
        validateSyntaxNode(node);
        Object.keys(node).forEach((key) => {
            if (node[key] &&
                (node[key] instanceof Object || (node[key]) instanceof Array)) {
                checkSyntaxTreeNodeTypes(node[key]);
            }
        });
    }
}
 
function validateCondition(conditionString) {
    try {
        const parseTree = esprima.parse(conditionString);
        if (!parseTree.body || parseTree.body.length !== 1 ) {
            throw {
                error: "invalid_policy",
                message: "'condition' must be exactly one non-empty Boolean expression."
            };
        } else {
            checkSyntaxTreeNodeTypes(parseTree.body);
        }
    } catch (e) {
        throw {
            error: "invalid_policy",
            message: `"Invalid 'condition': ${e.message}`
        };
    }
}
 
function validate(rule) {
    Iif (!rule.matchAnyOf) {
        throw {
            error: "invalid_policy",
            message: "Must have 'matchAnyOf'."
        };
    } else Iif (!rule.decision) {
        throw {
            error: "invalid_policy",
            message: "Must have 'decision'."
        };
    } else if (rule.condition || rule.condition === "") {
        validateCondition(rule.condition);
    }
}
 
module.exports = {
    validate
}