3

I am attempting to create a basic search that will return results from a Mongo database. The query should match values that are either the complete value or within the value. For example, a search of C should return C, C#, C++. Additionally, Java should return both Java, Javascript. I have a simple implementation currently set up that can accomplish this, however if I search for the query C++, I am returned with an internal server error. Searching C+ will return a successful search with C++ being listed. This only occurs with a ++ in the query, as searches with double special characters (//, --, $$, etc.) will return successfully.

I have attempted using the Javascript RegExp function to accomplish this, however I am new to regex and am sure this is where my problem is originating from. The current implementation

  1. Takes the search query from the input and encodes it
  2. Passes the query to the controller which applies the RegExp function
  3. Finds the results against the database and returns the results in a JSON object

AJAX Call for Search

function getSearchResults() {
    var query = $("#searchInput").val(); //search value
    if(query === '') {
        return false;
    } else {
        $.ajax({
            url: '/items/search?search=' + encodeURIComponent(query), //url with search query
            type: 'GET',
            dataType: 'json',
            beforeSend: function() {
                $('#loadingOverlay').css('visibility', 'visible');
            },
            success: function(result) {
                $('#itemContentMain').bootstrapTable({data: result});
                $('#itemContentMain').bootstrapTable('load', result);
                $('#loadingOverlay').css('visibility', 'hidden');
            }
        }).fail(function (xhr, ajaxOptions, thrownError){
            alert("The search failed to return results: " + thrownError);
        });
    }
    return false;
}

Controller

itemController.search = function(req, res) {
    var regexEx = new RegExp(req.query.search, "i"); //ignore case
    Item.find({ $or: [{name: regexEx}, {category: regexEx}, {subcategory: regexEx},
        {status: regexEx}, {description: regexEx}]}).exec(function (err, items) {
        if (err) {
            console.log("Error:", err);
        } else {
            console.log("Search returning results");
            res.send(items);
        }
   });
};

I have read that a ++ in Regex is a Possessive Quantifier so that if the engine fails matching, will not go back and try to undo the matches it made. However when the query is reaching the RegExp function, it is encoded as /items/search?search=%2B%2B. What is happening when the ++ is passed in the query, and how can I mitigate the internal server error that it is producing?

ryano
  • 185
  • 1
  • 13
  • If you want to use + literally (to match a plus character), you need escape it with `\ ` in the pattern. – marekful Jan 16 '19 at 17:28
  • I cannot stress enough how dangerous it is to construct a `RegExp` directly from user input. This leaves your server open to DDoS attacks. – Patrick Roberts Jan 16 '19 at 17:28

1 Answers1

2

+ has a special meaning in and cannot be used in the form ++. You need to escape the regex.

Using this function source:

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

you can replace:

var regexEx = new RegExp(req.query.search, "i"); //ignore case

with

var regexEx = new RegExp(escapeRegExp(req.query.search), "i"); //ignore case
                         ^^^^^^^^^^^^

You can repeat the error easily by creating a simple regex : /c++/ within the browser's console, which will throw the following message (may vary depending on the browser):

SyntaxError: nothing to repeat


Side note: this is quite a bad idea to allow user to search for raw input.

Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
  • This worked perfectly, thank you. As you and others have stated though, this is ultimately (very) bad practice. What would be a better method to go about using? – ryano Jan 16 '19 at 17:40
  • 1
    Well, @Ryan, escaping it will protect you against some DDoS attacks, which is a good first step. – Thomas Ayoub Jan 17 '19 at 08:22