browserify / static-eval Goto Github PK
View Code? Open in Web Editor NEWevaluate statically-analyzable expressions
License: MIT License
evaluate statically-analyzable expressions
License: MIT License
Hi @goto-bus-stop ,
there is a CVE in word-wrap: jonschlinkert/word-wrap#33
It is fixed and integrated in latest optionator 0.9.x, which is used in escodegen 2.x.
Is there any chance to update escodegen to 2.x?
Thanks!
[email protected]
│ └─┬ [email protected]
│ └── [email protected]
Optionator team will not merge the fix to 0.8.x: gkz/optionator#46
I can't bulkify code that looks like this because static-eval is throwing:
var uninitialized
, bulk = require('bulk-require')
, _ = require('lodash')
, conditions = bulk(__dirname, ['/*'])
Once I initialize the variable in the var statement, i get #10
Sorry if I'm being daft, but would this work with passing a three.vector in as a variable, and allowing the code to call methods on that? I want to allow declarative animations in @scenevr.
var src = 'v.multiplyScalar(t)';
var ast = parse(src).body[0].expression;
console.log(evaluate(ast, {
v: new THREE.Vector3,
t : new Date().valueOf()
}));
My test code looks like this:
var evaluate = require('static-eval');
var parse = require('esprima').parse;
var src = 'foo(function (obj) { return obj.x })';
var ast = parse(src).body[0].expression;
var result = evaluate(ast, {
foo: function (func) {
return func({x: 1})
},
});
console.log(result);
When I run it, I get:
$ node test.js
/home/stathis/src/jellyfish-jellyscript/node_modules/static-eval/index.js:134
return obj[node.property.name];
^
TypeError: Cannot read property 'x' of null
at walk (/home/stathis/src/jellyfish-jellyscript/node_modules/static-eval/index.js:134:27)
at walk (/home/stathis/src/jellyfish-jellyscript/node_modules/static-eval/index.js:152:20)
at walk (/home/stathis/src/jellyfish-jellyscript/node_modules/static-eval/index.js:171:20)
at walk (/home/stathis/src/jellyfish-jellyscript/node_modules/static-eval/index.js:112:25)
at module.exports (/home/stathis/src/jellyfish-jellyscript/node_modules/static-eval/index.js:204:7)
at Object.<anonymous> (/home/stathis/src/jellyfish-jellyscript/test.js:6:14)
at Module._compile (internal/modules/cjs/loader.js:1068:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
at Module.load (internal/modules/cjs/loader.js:933:32)
at Function.Module._load (internal/modules/cjs/loader.js:774:14)
whereas when I change
var src = 'foo(function (obj) { return obj.x })';`
to
var src = 'foo(function (obj) { return obj && obj.x })';`
I get a successful result:
$ node test.js
1
This looks like a bug to me, as my callback's input here is just {x: 1}
, why would I need to make sure obj
exists?
Looks like GitHub picked up the withdrawn npm issue:
It's marked as withdrawn on GitHub, but I'm still getting the critical errors from npm audit
.
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Critical │ Withdrawn: Arbitrary Code Execution in static-eval │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ static-eval │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ No patch available │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ pdfkit │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ pdfkit > fontkit > brfs > static-module > static-eval │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://github.com/advisories/GHSA-8v27-2fg9-7h62 │
└───────────────┴──────────────────────────────────────────────────────────────┘
Hello,
Sorry if this is a silly question, but what would be the difference between this library and using eval or Function?
Also, what does statically-analyzable expressions mean?
Thanks,
Why does the following work:
[1, 2].map(function(x) { return x + 1 })
But not the following:
[{a: 1}, {a: 2}].map(function(x) { return x["a"] })
Since .map()
does not seem to be an issue, and member expressions of course aren’t, I’m not sure why this fails. The actual error is TypeError: Cannot read properties of null (reading 'a')
The inner function ASTs are respectively:
ReturnStatement {
type: 'ReturnStatement',
argument: BinaryExpression {
type: 'BinaryExpression',
operator: '+',
left: Identifier { type: 'Identifier', name: 'x' },
right: Literal { type: 'Literal', value: 1, raw: '1' }
}
}
And:
ReturnStatement {
type: 'ReturnStatement',
argument: ComputedMemberExpression {
type: 'MemberExpression',
computed: true,
object: Identifier { type: 'Identifier', name: 'x' },
property: Literal { type: 'Literal', value: 'a', raw: '"a"' }
}
}
Statically evaluating a MemberExpression is not harder than a BinaryExpression, is it?
After looking at the code, it seems to me that in both cases the x
statement is statically evaluated to null
. In the BinaryExpression
code, the binary evaluation returns null + 1
which is 1
, and you then allow and execute the function.
However since null["a"]
raises an error, the function is not allowed and executed. However x["a"]
is knowable statically (it is either the property or undefined
if it’s absent − if you call evaluate('x["b"]', {x: {a: 1}})
, you get undefined
, not an error), so the function should execute in this case as well.
There is a simple workaround which is to return (x || {a: ""})["a"]
instead of x["a"]
, this avoids throwing an error by ensuring there is a fallback value to x.a
if x
is undefined.
I've created a module that uses static-eval. In node.js, it's working great. When I try to use webpack to create a bundle including my module, esprima and static-eval, I get this error:
require function is used in a way in which dependencies cannot be statically extracted
If I comment out references to static-eval, webpack runs fine, so it looks like the problem is in static-eval. I'm using webpack 2.5.1. Do you know of a solution?
Hello,
I'm updating dependencies in my project (yay!) but I'm having some issues with the Slack Node SDK and @vercel/ncc
.
I am maintaining a serverless function that reacts to interactive Slack messages. I am using the Slack SDK in a TypeScript environment, compiling my code with ncc
for deployment to AWS Lambda.
Updating ncc
also updates static-eval
, which throws a new error when running my compiled Lambda function.
Going through the change logs between the old and new versions I believe this was introduced in #27
The error I am seeing is Error: Attempt to inherit from WebClient methods without inheriting from WebClient
Here is how I am using the Slack SDK:
import {WebClient as SlackWebClient} from '@slack/web-api'
(async () => {
// The slack token is fetched from SSM
const {slackToken} = await getSecrets({environment})
const slackClient = new SlackWebClient(slackToken)
await slackClient.chat.postMessage({
channel: channelID,
thread_ts: messageID,
text: `Some message text`,
})
})
which aligns with their Posting a message with Web API documentation
If you can point me in the right direction on how to resolve this I would appreciate it.
This is a good project but it's very hard to know what's wrong. We only know when there is a problem by getting undefined.. But if the expression does not return anything and only have a side effect then we don't even know if the code was executed or if there was a problem in the AST.
I suggest static-eval
could use Errors
instead of the FAIL object. This way it would not be needed to check everytime of something is equal fo FAIL
and the end user would get more information about the error.
For example I spent 30 minutes figuring out why I was receiving undefined
for a perfectly valid expression. It was simply that 'NewExpression' was not handled (This is not a problem of course, this is open source community someone else can implement that) but with Errors I would have known right away.
Would you accept a pull request using Error
instead of FAIL
?
Here is my code snippet:
const evaluate = require('static-eval');
const parse = require('esprima').parse;
const src = 'Date.now()';
const ast = parse(src).body[0].expression;
const result = evaluate(ast, { Date: Date });
console.log(result);
The code Date.now()
will produce an AST tree as shown below:
{
"type": "Program",
"body": [
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"computed": false,
"object": {
"type": "Identifier",
"name": "Date"
},
"property": {
"type": "Identifier",
"name": "now"
}
},
"arguments": []
}
}
],
"sourceType": "script"
}
After tracing into the code, I found that there was a security check added in the commit: c06f1b8#diff-168726dbe96b3ce427e7fedce31bb0bcR101
Actually, the type of Date
is a function, it will return FAIL and result in undefined result:
// do not allow access to methods on Function
if((obj === FAIL) || (typeof obj == 'function')){
return FAIL;
}
Remove typeof obj === 'function'
from if statement will definitely resolve this issue:
if(obj === FAIL){
return FAIL;
}
May I know if there is a security reason that should not allow callee object as a function?
hey I found a gap where you can get at Function
and then create code full non static code.
var evaluate = require('./');
var parse = require('esprima').parse;
var src = '(function () {}).constructor("var l = 3; while(l--) console.log(l); console.log(Date.now()); arguments.callee()")()';
var ast = parse(src).body[0].expression;
var res = evaluate(ast); //RangeError from stack overflow
console.log(res)
here is the output:
2
1
0
1426154732632
2
1
0
1426154732632
2
1
...
RangeError: Maximum call stack size exceeded
as you would expect.
It's somewhat harder to prevent turing completeness than it is to create it.
Issue picked up and reported in Snyk: https://snyk.io/vuln/SNYK-JS-STATICEVAL-1056765
Declaring a function causes the function to be invoked. This is particularly nasty for array iteration methods, where the iterator will get invoked an extra time with a null value.
Here is a test that pinpoints the issue:
test('function declaration does not invoke function', function(t) {
t.plan(1);
var invoked = false;
var variables = {
noop: function(){},
onInvoke: function() {invoked = true}
};
var src = `noop(function(){ onInvoke(); })`;
var ast = parse(src).body[0].expression;
evaluate(ast, variables);
t.equal(invoked, false);
})
Here is a failing test that demonstrates the array use case:
test('array methods invocation count', function(t) {
t.plan(2);
var variables = {
values: [1, 2, 3],
receiver: []
};
var src = 'values.forEach(function(x) { receiver.push(x); })'
var ast = parse(src).body[0].expression;
evaluate(ast, variables);
t.equal(variables.receiver.length, 3);
t.deepEqual(variables.receiver, [1, 2, 3]);
})
The result of the deepEqual check is:
operator: deepEqual
expected: [ 1, 2, 3 ]
actual: [ null, 1, 2, 3 ]
I can't bulkify code that looks like this because static-eval is throwing:
var bulk = require('bulk-require')
, _ = require('lodash')
, conditions = bulk(__dirname, ['/*.js'])
However, this works:
var bulk = require('bulk-require')
var _ = require('lodash')
var conditions = bulk(__dirname, ['/*.js'])
Hi,
We have updated the static-eval package to the latest version(2.1.0)
The latest version of static-eval has 1 Critical Vulnerability (CVE-2021-23334) with score 9.8.
Is there a way to fix this vulnerability? If so, can you please let us know how to do?
And Latest Version was published on 06/15/2020. Can you please also state the release date of next version?
I'm trying to do something like this:
var esprima = require('esprima'),
staticEval = require('static-eval'),
ast = esprima.parse('[1, 2, 3].map(function(x) { return x * foo; })');
console.log(staticEval(ast.body[0].expression, {foo: 100}));
which throws:
ReferenceError: foo is not defined
at eval (eval at walk (/Users/allens/work/data-expression/node_modules/static-eval/index.js:107:20), <anonymous>:3:16)
at Array.map (native)
at walk (/Users/allens/work/data-expression/node_modules/static-eval/index.js:89:27)
at module.exports (/Users/allens/work/data-expression/node_modules/static-eval/index.js:110:7)
at repl:1:13
at REPLServer.self.eval (repl.js:110:21)
at repl.js:249:20
at REPLServer.self.eval (repl.js:122:7)
at Interface.<anonymous> (repl.js:239:12)
at Interface.emit (events.js:95:17)
It looks like this is because functions are generated via escodegen rather than walking the AST of the FunctionExpression
(which is, admittedly, a pain in the butt). A hacky way to fix this might be to create a function with arguments and apply it with the local vars, like this (untested):
var body = unparse(node),
keys = Object.keys(vars),
values = keys.map(function(key) { return vars[key]; }),
fn = new Function(keys.join(', '), 'return ' + body);
return fn.apply(null, values);
I was hoping to use static-eval to execute user input without eval
. The user can write expressions using a build-in set of pre-defined functions passed into static-eval's evaluate. However the readme states:
static-eval is like eval. It is intended for use in build scripts and code transformations, doing some evaluation at build time—it is NOT suitable for handling arbitrary untrusted user input. Malicious user input can execute arbitrary code.
How could this be done? I couldn't see how it would be possible.
Does this warning date back to before the 2.0.0 release (i.e. this vunerability: https://maustin.net/articles/2017-10/static_eval) and not apply with the current version?
const { parse } = require('esprima');
const staticEval = require('static-eval');
const ast = parse('(() => 1)()');
const val = staticEval(ast.body[0].expression, {}, { allowAccessToMethodsOnFunctions: true });
// val is undefined
The expression I was actually trying to evaluate looks more like '[1,2,3].map(x => x)'
Is there some workaround?
// make pollution
const evaluate = require('static-eval');
const parse = require('esprima').parse;
var src = `({})['__proto__']['__defineGetter__']('toString', ({})['constructor'])`
var ast = parse(src).body[0].expression;
evaluate(ast);
// serve webapp
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.end('working!');
});
app.listen(8080);
details in
https://blog.p6.is/bypassing-a-js-sandbox/#Prototype-Pollution-to-Remote-Code-Execution
Do you accept pull requests ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.