1

I am currently working on a "simple" tax calculator that gets some information from a database and the web page it is currently on hosted on. For example, you are on a property page that has details about your home. It would find specific pieces of information: tax district, property type, and current value to name a few. On top of this, the database has some changing information that is entered through an admin interface to develop the tax formula.

I am experimenting with eval to process the formula that is return from the database via jsonp. The issue I am running into is how can I get the eval function to recognize that the variables that the formula is comprised of are in the "calculator" object.

Example json results:

var calculator = {
  "Name": "Residential",
  "Formula": "(((rrb * (base + iv) - (hcv * homestead)) - (mcv * military)) * (levy / 1000))",
  "Levy": 1000.00000,
  "Variables": [
    {
      "Type": "System",
      "DisplayName": "Tax District",
      "ShortName": "levy",
      "Value": 0.000000
    },
    {
      "Type": "Form Variable",
      "DisplayName": "Current Property Value",
      "ShortName": "currentValue",
      "Value": 0.000000
    },
    {
      "Type": "Form Variable",
      "DisplayName": "Number of Homestead Credits",
      "ShortName": "homestead",
      "Value": 0.000000
    },
    {
      "Type": "Form Variable",
      "DisplayName": "Number of Military Credits",
      "ShortName": "military",
      "Value": 0.000000
    },
    {
      "Type": "Rollback",
      "DisplayName": "Residential Rollback",
      "ShortName": "rrb",
      "Value": 0.528200
    },
    {
      "Type": "User Input",
      "DisplayName": "Total Value Add of Improvements",
      "ShortName": "iv",
      "Value": 0.000000
    },
    {
      "Type": "Credit",
      "DisplayName": "Homestead Credit Value",
      "ShortName": "hcv",
      "Value": 4850.000000
    },
    {
      "Type": "Credit",
      "DisplayName": "Military Credit Value",
      "ShortName": "mcv",
      "Value": 1852.000000
    }
  ]
}

With the above object, the formula used with eval, will only look at the global namespace, but I really don't want to expose the variables that way.

What is the easiest way to get this to play nicely with eval? Ideally, I could have some function I pass the object into and it would make the values easily available to the eval function.

Justin
  • 3,337
  • 3
  • 16
  • 27
  • You've only shown us half of the problem. How specifically are the quantities from the page stored? In named JavaScript variables? In the DOM? If the latter, how do you expect to match them with the JSONP? `data-` attributes? HTML ids? etc. – Stephen Thomas Jan 07 '14 at 16:42
  • The string above is not really JSON. JSON would not have the variable declaration nor the assignment preceding it. It would begin with the brace and end with the last brace. Take a look at http://stackoverflow.com/questions/4935632/how-to-parse-json-in-javascript for pointers at parsing JSON. – Jon Trauntvein Jan 07 '14 at 16:43
  • @StephenThomas They are stored in the same object returned from the request. Anything of type `Form Variable` are queried by the `shortName` as the ID and the value is stored in the `Value` property of the json object. – Justin Jan 07 '14 at 16:43
  • @JonTrauntvein The point of that we to show that the resulting JSON is stored in a variable called `calculator`. – Justin Jan 07 '14 at 16:44
  • Why not have your server do the calculations for you and return you the answer in the response? – gen_Eric Jan 07 '14 at 16:45
  • @RocketHazmat I want a more responsive design than a `POST` or `AJAX` operation. Even then, I still have the issue of taking the values and formula and executing the formula. – Justin Jan 07 '14 at 16:49
  • @Justin: Where is the formula coming from? Can you just make it a function in your JavaScript and then call it with the correct values? – gen_Eric Jan 07 '14 at 16:51
  • @RocketHazmat The formula is created through an admin interface. It gives the drag and drop capabilities and generates the formula as they plug in the pieces. The variables are different from calculator to calculator. EG: Residential, Business, Agricultural, etc... – Justin Jan 07 '14 at 16:53
  • 1
    Did you mean something [like this](http://jsfiddle.net/rU643/4/)? – Andy Jan 07 '14 at 16:58

3 Answers3

1

Okay, first off: don't do this. It is pretty much never safe to eval anything the user gives you as it opens you up to all kinds of nasty attack vectors.

Seriously. Don't do this. Write a parser that'll do this for you. It'll be a pain in the ass but I guarantee you'll be doing this eventually anyway once the gaping security holes become evident.

But, if you're still dead set on doing this:

eval can't be told to use a new context using eval.call or eval.apply. You have to massage the scope yourself by defining a function that you call within a specific object context.

Take this code:

var o = {
  s: 'catpants',
  t: 'doggyhat',
  f: function(statement) {
    return eval(statement);
  }
}

By calling o.f("this.s + ' ' + this.t");, you'll get catpants doggyhat.

For your situation, you'd have to define an object that resolves all the variables the user might use in an expression, then define a function on that object that evals the user's statement.

drhayes
  • 581
  • 3
  • 16
  • I'm not sure I with not using `eval` as it is running the client machine so if they do enter something "malicious", it wouldn't cause any server side code to execute, just on their own machine. Very nice answer though! – Justin Jan 07 '14 at 17:04
  • If one user can write an expression that other users can see this opens a cross-site scripting hole: http://en.wikipedia.org/wiki/Cross-site_scripting That means that I could get you to open one of these spreadsheets that then uses your credentials to do something malicious. – drhayes Jan 07 '14 at 17:18
  • User's externally couldn't do damage, but I do see your point in someone that uses the admin interface to create the formula and enters the value for the variables has less than noble intentions, it could pose a problem, thanks. – Justin Jan 07 '14 at 17:25
1

Probably This may help you:

var calculator = {
  Name: "Residential",
  Formula : function(){
      return (((rrb * (base + iv) - (hcv * homestead)) - (mcv * military)) * (levy / 1000));
  },
  Levy: 1000.00000,
  Variables: [
    {
      "Type": "System",
      "DisplayName": "Tax District",
      "ShortName": "levy",
      "Value": 0.000000
    },
    {
      "Type": "Form Variable",
      "DisplayName": "Current Property Value",
      "ShortName": "currentValue",
      "Value": 0.000000
    },
    {
      "Type": "Form Variable",
      "DisplayName": "Number of Homestead Credits",
      "ShortName": "homestead",
      "Value": 0.000000
    },
    {
      "Type": "Form Variable",
      "DisplayName": "Number of Military Credits",
      "ShortName": "military",
      "Value": 0.000000
    },
    {
      "Type": "Rollback",
      "DisplayName": "Residential Rollback",
      "ShortName": "rrb",
      "Value": 0.528200
    },
    {
      "Type": "User Input",
      "DisplayName": "Total Value Add of Improvements",
      "ShortName": "iv",
      "Value": 0.000000
    },
    {
      "Type": "Credit",
      "DisplayName": "Homestead Credit Value",
      "ShortName": "hcv",
      "Value": 4850.000000
    },
    {
      "Type": "Credit",
      "DisplayName": "Military Credit Value",
      "ShortName": "mcv",
      "Value": 1852.000000
    }
  ]
}

I am assuming your variables rrb, base, iv, hcv, homestead, mcv, military, levy are defined globally.

Ashish Kumar
  • 2,991
  • 3
  • 18
  • 27
0

You'll need to write a parser for your formula. There is no automatic/easy way to do this.

Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176