Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

json-logic-js

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-logic-js - npm Package Compare versions

Comparing version 2.0.1 to 2.0.2

8

CHANGELOG.md
# Change Log
## 2.0.2
Thanks [@panzi](https://github.com/panzi) for rebuilding the test system and removing Gulp as a dev dependency.
## 2.0.1
The operations object could be exploited to run arbitrary code. Resolves [SNYK-JS-JSONLOGICJS-674308](https://security.snyk.io/vuln/SNYK-JS-JSONLOGICJS-674308), thanks Arel Cordero for reporting.
## 2.0.0

@@ -4,0 +12,0 @@

34

logic.js

@@ -223,3 +223,2 @@ /* globals define,module */

var scopedData;
var filtered;
var initial;

@@ -318,3 +317,3 @@

// All of an empty set is false. Note, some and none have correct fallback after the for loop
if ( ! scopedData.length) {
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
return false;

@@ -329,7 +328,27 @@ }

} else if (op === "none") {
filtered = jsonLogic.apply({filter: values}, data);
return filtered.length === 0;
scopedData = jsonLogic.apply(values[0], data);
scopedLogic = values[1];
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
return true;
}
for (i=0; i < scopedData.length; i+=1) {
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
return false; // First truthy, short circuit
}
}
return true; // None were truthy
} else if (op === "some") {
filtered = jsonLogic.apply({filter: values}, data);
return filtered.length > 0;
scopedData = jsonLogic.apply(values[0], data);
scopedLogic = values[1];
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
return false;
}
for (i=0; i < scopedData.length; i+=1) {
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
return true; // First truthy, short circuit
}
}
return false; // None were truthy
}

@@ -352,3 +371,2 @@

for (i = 0; i < sub_ops.length; i++) {
if (!operation.hasOwnProperty(sub_ops[i])) {

@@ -384,3 +402,3 @@ throw new Error("Unrecognized operation " + op +

// Recursion!
values.map(function(val) {
values.forEach(function(val) {
collection.push.apply(collection, jsonLogic.uses_data(val) );

@@ -387,0 +405,0 @@ });

{
"name": "json-logic-js",
"version": "2.0.1",
"version": "2.0.2",
"description": "Build complex rules, serialize them as JSON, and execute them in JavaScript",

@@ -9,12 +9,9 @@ "main": "logic.js",

},
"dependencies": {},
"devDependencies": {
"eslint": "^7.11.0",
"eslint": "^7.32.0",
"eslint-config-google": "^0.14.0",
"gulp": "^3.9.0",
"qunit": "^0.7.7",
"request": "^2.65.0"
"qunit": "^2.16.0"
},
"scripts": {
"test": "gulp test"
"test": "qunit ./tests/tests.js"
},

@@ -21,0 +18,0 @@ "repository": {

@@ -0,284 +1,310 @@

var QUnit = require("qunit");
var jsonLogic = require("../logic.js");
var http = require("http");
var https = require("https");
var fs = require("fs");
var path = require("path");
var download = function(url, dest, cb) {
async function download(url, dest) {
var file = fs.createWriteStream(dest);
http.get(url, function(response) {
response.pipe(file);
file.on("finish", function() {
file.close(cb); // close() is async, call cb after close completes.
return new Promise((resolve, reject) => {
https.get(url, function(response) {
if (response.statusCode !== 200) {
return reject(new Error(response.statusCode + ' ' + response.statusMessage));
}
response.pipe(file);
file.on("finish", function() {
// close() is async, call cb after close completes.
file.close(err => err ? reject(err) : resolve());
});
}).on("error", function(err) { // Handle errors
fs.unlink(dest, () => {
// Delete the file async. (But we don't check the result)
reject(err);
});
});
}).on("error", function(err) { // Handle errors
fs.unlink(dest); // Delete the file async. (But we don't check the result)
if (cb) cb(err.message);
});
};
}
var remote_or_cache = function(remote_url, local_file, description, runner) {
var parse_and_iterate = function(local_file, description, runner) {
fs.readFile(local_file, "utf8", function(error, body) {
var tests;
try {
tests = JSON.parse(body);
} catch (e) {
throw new Error("Trouble parsing " + description + ": " + e.message);
}
async function remote_or_cache(remote_url, local_file, description) {
local_file = path.join(__dirname, local_file);
async function parse_and_iterate(local_file, description) {
var body = await fs.promises.readFile(local_file, "utf8");
var tests;
try {
tests = JSON.parse(body);
} catch (e) {
throw new Error("Trouble parsing " + description + ": " + e.message);
}
// Remove comments
tests = tests.filter(function(test) {
return typeof test !== "string";
});
// Remove comments
tests = tests.filter(test => typeof test !== "string");
console.log("Including "+tests.length+" "+description);
console.log("Including "+tests.length+" "+description);
return tests;
}
QUnit.test(description, function(assert) {
tests.map(runner);
});
try {
await fs.promises.stat(local_file);
} catch (err) {
console.log("Downloading " + description + " from JsonLogic.com");
await download(remote_url, local_file);
return parse_and_iterate(local_file, description);
}
start();
console.log("Using cached " + description);
return parse_and_iterate(local_file, description);
};
var appliesTests;
QUnit.module("applies() tests", {
before(assert) {
var done = assert.async();
remote_or_cache(
"https://jsonlogic.com/tests.json",
"tests.json",
"applies() tests"
).then(tests => {
appliesTests = tests;
done();
});
};
}
}, () => {
QUnit.test("all", (assert) => {
for (const test of appliesTests) {
var rule = test[0];
var data = test[1];
var expected = test[2];
// Only waiting on the request() is async
stop();
fs.stat(local_file, function(err, stats) {
if (err) {
console.log("Downloading " + description + " from JsonLogic.com");
download(remote_url, local_file, function() {
parse_and_iterate(local_file, description, runner);
});
} else {
console.log("Using cached " + description);
parse_and_iterate(local_file, description, runner);
assert.deepEqual(
jsonLogic.apply(rule, data),
expected,
"jsonLogic.apply("+ JSON.stringify(rule) +"," +
JSON.stringify(data) +") === " +
JSON.stringify(expected)
);
}
});
};
});
remote_or_cache(
"http://jsonlogic.com/tests.json",
"tests.json",
"applies() tests",
function(test) {
var rule = test[0];
var data = test[1];
var expected = test[2];
var ruleTests;
QUnit.module("rule_like() tests", {
before(assert) {
var done = assert.async();
assert.deepEqual(
jsonLogic.apply(rule, data),
expected,
"jsonLogic.apply("+ JSON.stringify(rule) +"," +
JSON.stringify(data) +") === " +
JSON.stringify(expected)
);
remote_or_cache(
"https://jsonlogic.com/rule_like.json",
"rule_like.json",
"rule_like() tests"
).then(tests => {
ruleTests = tests;
done();
})
}
);
}, () => {
QUnit.test("all", (assert) => {
for (const test of ruleTests) {
var rule = test[0];
var pattern = test[1];
var expected = test[2];
remote_or_cache(
"http://jsonlogic.com/rule_like.json",
"rule_like.json",
"rule_like() tests",
function(test) {
var rule = test[0];
var pattern = test[1];
var expected = test[2];
assert.deepEqual(
jsonLogic.rule_like(rule, pattern),
expected,
"jsonLogic.rule_like("+ JSON.stringify(rule) +"," +
JSON.stringify(pattern) +") === " +
JSON.stringify(expected)
);
}
});
});
assert.deepEqual(
jsonLogic.rule_like(rule, pattern),
expected,
"jsonLogic.rule_like("+ JSON.stringify(rule) +"," +
JSON.stringify(pattern) +") === " +
JSON.stringify(expected)
QUnit.module('basic', () => {
QUnit.test( "Bad operator", function( assert ) {
assert.throws(
function() {
jsonLogic.apply({"fubar": []});
},
/Unrecognized operation/
);
}
);
});
QUnit.test( "Bad operator", function( assert ) {
assert.throws(
function() {
jsonLogic.apply({"fubar": []});
},
/Unrecognized operation/
);
});
QUnit.test( "logging", function( assert ) {
var last_console;
console.log = function(logged) {
last_console = logged;
};
assert.equal( jsonLogic.apply({"log": [1]}), 1 );
assert.equal( last_console, 1 );
});
QUnit.test( "edge cases", function( assert ) {
assert.equal( jsonLogic.apply(), undefined, "Called with no arguments" );
QUnit.test( "logging", function( assert ) {
var last_console;
console.log = function(logged) {
last_console = logged;
};
assert.equal( jsonLogic.apply({"log": [1]}), 1 );
assert.equal( last_console, 1 );
});
assert.equal( jsonLogic.apply({ var: "" }, 0), 0, "Var when data is 'falsy'" );
assert.equal( jsonLogic.apply({ var: "" }, null), null, "Var when data is null" );
assert.equal( jsonLogic.apply({ var: "" }, undefined), undefined, "Var when data is undefined" );
QUnit.test( "edge cases", function( assert ) {
assert.equal( jsonLogic.apply(), undefined, "Called with no arguments" );
assert.equal( jsonLogic.apply({ var: ["a", "fallback"] }, undefined), "fallback", "Fallback works when data is a non-object" );
});
assert.equal( jsonLogic.apply({ var: "" }, 0), 0, "Var when data is 'falsy'" );
assert.equal( jsonLogic.apply({ var: "" }, null), null, "Var when data is null" );
assert.equal( jsonLogic.apply({ var: "" }, undefined), undefined, "Var when data is undefined" );
QUnit.test( "Expanding functionality with add_operator", function( assert) {
// Operator is not yet defined
assert.throws(
function() {
jsonLogic.apply({"add_to_a": []});
},
/Unrecognized operation/
);
assert.equal( jsonLogic.apply({ var: ["a", "fallback"] }, undefined), "fallback", "Fallback works when data is a non-object" );
});
// Set up some outside data, and build a basic function operator
var a = 0;
var add_to_a = function(b) {
if (b === undefined) {
b=1;
} return a += b;
};
jsonLogic.add_operation("add_to_a", add_to_a);
// New operation executes, returns desired result
// No args
assert.equal( jsonLogic.apply({"add_to_a": []}), 1 );
// Unary syntactic sugar
assert.equal( jsonLogic.apply({"add_to_a": 41}), 42 );
// New operation had side effects.
assert.equal(a, 42);
QUnit.test( "Expanding functionality with add_operator", function( assert) {
// Operator is not yet defined
assert.throws(
function() {
jsonLogic.apply({"add_to_a": []});
},
/Unrecognized operation/
);
var fives = {
add: function(i) {
return i + 5;
},
subtract: function(i) {
return i - 5;
},
};
// Set up some outside data, and build a basic function operator
var a = 0;
var add_to_a = function(b) {
if (b === undefined) {
b=1;
} return a += b;
};
jsonLogic.add_operation("add_to_a", add_to_a);
// New operation executes, returns desired result
// No args
assert.equal( jsonLogic.apply({"add_to_a": []}), 1 );
// Unary syntactic sugar
assert.equal( jsonLogic.apply({"add_to_a": 41}), 42 );
// New operation had side effects.
assert.equal(a, 42);
jsonLogic.add_operation("fives", fives);
assert.equal( jsonLogic.apply({"fives.add": 37}), 42 );
assert.equal( jsonLogic.apply({"fives.subtract": [47]}), 42 );
var fives = {
add: function(i) {
return i + 5;
},
subtract: function(i) {
return i - 5;
},
};
// Calling a method with multiple var as arguments.
jsonLogic.add_operation("times", function(a, b) {
return a*b;
});
assert.equal(
jsonLogic.apply(
{"times": [{"var": "a"}, {"var": "b"}]},
{a: 6, b: 7}
),
42
);
jsonLogic.add_operation("fives", fives);
assert.equal( jsonLogic.apply({"fives.add": 37}), 42 );
assert.equal( jsonLogic.apply({"fives.subtract": [47]}), 42 );
// Remove operation:
jsonLogic.rm_operation("times");
// Calling a method with multiple var as arguments.
jsonLogic.add_operation("times", function(a, b) {
return a*b;
assert.throws(
function() {
jsonLogic.apply({"times": [2, 2]});
},
/Unrecognized operation/
);
// Calling a method that takes an array, but the inside of the array has rules, too
jsonLogic.add_operation("array_times", function(a) {
return a[0]*a[1];
});
assert.equal(
jsonLogic.apply(
{"array_times": [[{"var": "a"}, {"var": "b"}]]},
{a: 6, b: 7}
),
42
);
});
assert.equal(
jsonLogic.apply(
{"times": [{"var": "a"}, {"var": "b"}]},
{a: 6, b: 7}
),
42
);
// Remove operation:
jsonLogic.rm_operation("times");
QUnit.test("Control structures don't eval depth-first", function(assert) {
// Depth-first recursion was wasteful but not harmful until we added custom operations that could have side-effects.
assert.throws(
function() {
jsonLogic.apply({"times": [2, 2]});
},
/Unrecognized operation/
);
// If operations run the condition, if truthy, it runs and returns that consequent.
// Consequents of falsy conditions should not run.
// After one truthy condition, no other condition should run
var conditions = [];
var consequents = [];
jsonLogic.add_operation("push.if", function(v) {
conditions.push(v); return v;
});
jsonLogic.add_operation("push.then", function(v) {
consequents.push(v); return v;
});
jsonLogic.add_operation("push.else", function(v) {
consequents.push(v); return v;
});
// Calling a method that takes an array, but the inside of the array has rules, too
jsonLogic.add_operation("array_times", function(a) {
return a[0]*a[1];
});
assert.equal(
jsonLogic.apply(
{"array_times": [[{"var": "a"}, {"var": "b"}]]},
{a: 6, b: 7}
),
42
);
});
jsonLogic.apply({"if": [
{"push.if": [true]},
{"push.then": ["first"]},
{"push.if": [false]},
{"push.then": ["second"]},
{"push.else": ["third"]},
]});
assert.deepEqual(conditions, [true]);
assert.deepEqual(consequents, ["first"]);
QUnit.test("Control structures don't eval depth-first", function(assert) {
// Depth-first recursion was wasteful but not harmful until we added custom operations that could have side-effects.
conditions = [];
consequents = [];
jsonLogic.apply({"if": [
{"push.if": [false]},
{"push.then": ["first"]},
{"push.if": [true]},
{"push.then": ["second"]},
{"push.else": ["third"]},
]});
assert.deepEqual(conditions, [false, true]);
assert.deepEqual(consequents, ["second"]);
// If operations run the condition, if truthy, it runs and returns that consequent.
// Consequents of falsy conditions should not run.
// After one truthy condition, no other condition should run
var conditions = [];
var consequents = [];
jsonLogic.add_operation("push.if", function(v) {
conditions.push(v); return v;
});
jsonLogic.add_operation("push.then", function(v) {
consequents.push(v); return v;
});
jsonLogic.add_operation("push.else", function(v) {
consequents.push(v); return v;
});
conditions = [];
consequents = [];
jsonLogic.apply({"if": [
{"push.if": [false]},
{"push.then": ["first"]},
{"push.if": [false]},
{"push.then": ["second"]},
{"push.else": ["third"]},
]});
assert.deepEqual(conditions, [false, false]);
assert.deepEqual(consequents, ["third"]);
jsonLogic.apply({"if": [
{"push.if": [true]},
{"push.then": ["first"]},
{"push.if": [false]},
{"push.then": ["second"]},
{"push.else": ["third"]},
]});
assert.deepEqual(conditions, [true]);
assert.deepEqual(consequents, ["first"]);
conditions = [];
consequents = [];
jsonLogic.apply({"if": [
{"push.if": [false]},
{"push.then": ["first"]},
{"push.if": [true]},
{"push.then": ["second"]},
{"push.else": ["third"]},
]});
assert.deepEqual(conditions, [false, true]);
assert.deepEqual(consequents, ["second"]);
jsonLogic.add_operation("push", function(arg) {
i.push(arg); return arg;
});
var i = [];
conditions = [];
consequents = [];
jsonLogic.apply({"if": [
{"push.if": [false]},
{"push.then": ["first"]},
{"push.if": [false]},
{"push.then": ["second"]},
{"push.else": ["third"]},
]});
assert.deepEqual(conditions, [false, false]);
assert.deepEqual(consequents, ["third"]);
i = [];
jsonLogic.apply({"and": [{"push": [false]}, {"push": [false]}]});
assert.deepEqual(i, [false]);
i = [];
jsonLogic.apply({"and": [{"push": [false]}, {"push": [true]}]});
assert.deepEqual(i, [false]);
i = [];
jsonLogic.apply({"and": [{"push": [true]}, {"push": [false]}]});
assert.deepEqual(i, [true, false]);
i = [];
jsonLogic.apply({"and": [{"push": [true]}, {"push": [true]}]});
assert.deepEqual(i, [true, true]);
jsonLogic.add_operation("push", function(arg) {
i.push(arg); return arg;
i = [];
jsonLogic.apply({"or": [{"push": [false]}, {"push": [false]}]});
assert.deepEqual(i, [false, false]);
i = [];
jsonLogic.apply({"or": [{"push": [false]}, {"push": [true]}]});
assert.deepEqual(i, [false, true]);
i = [];
jsonLogic.apply({"or": [{"push": [true]}, {"push": [false]}]});
assert.deepEqual(i, [true]);
i = [];
jsonLogic.apply({"or": [{"push": [true]}, {"push": [true]}]});
assert.deepEqual(i, [true]);
});
var i = [];
i = [];
jsonLogic.apply({"and": [{"push": [false]}, {"push": [false]}]});
assert.deepEqual(i, [false]);
i = [];
jsonLogic.apply({"and": [{"push": [false]}, {"push": [true]}]});
assert.deepEqual(i, [false]);
i = [];
jsonLogic.apply({"and": [{"push": [true]}, {"push": [false]}]});
assert.deepEqual(i, [true, false]);
i = [];
jsonLogic.apply({"and": [{"push": [true]}, {"push": [true]}]});
assert.deepEqual(i, [true, true]);
i = [];
jsonLogic.apply({"or": [{"push": [false]}, {"push": [false]}]});
assert.deepEqual(i, [false, false]);
i = [];
jsonLogic.apply({"or": [{"push": [false]}, {"push": [true]}]});
assert.deepEqual(i, [false, true]);
i = [];
jsonLogic.apply({"or": [{"push": [true]}, {"push": [false]}]});
assert.deepEqual(i, [true]);
i = [];
jsonLogic.apply({"or": [{"push": [true]}, {"push": [true]}]});
assert.deepEqual(i, [true]);
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc