简体   繁体   中英

Parse JSON-like input containing /regexp/ literals

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

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/

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM