4

I am trying to make a whitelist of allowed url args/query strings so any provided args in the url that are not in my whitelist are deleted from the url.

Here is my code.

var paramsString = "2=lol&q=how&44=slap&topic=api&1=tr&view=media"; //test url args
var searchParams = new URLSearchParams(paramsString);

//this whitelist of args are the only args to be allowed in the url
var url_args_whitelist = [
"beforeafter",
"catid",
"childforums",
"display",
"element_id",
"element_type",
"exactname",
"filter_mediaType",
"filter_order",
"filter_order_Dir",
"filter_search",
"filter_tag",
"format",
"id",
"Itemid",
"layout",
"limit",
"limitstart",
"messageid",
"more",
"option",
"order",
"ordering",
"quality",
"query",
"recently",
"recip",
"reply_id",
"return",
"searchdate",
"searchf",
"searchphrase",
"searchuser",
"searchword",
"sortby",
"start",
"task",
"tmpl",
"token",
"view"
];

for (let p of searchParams) {
//if the url argument is not in our whitelist of allowed arguments then delete it
  searchParams.delete(p[0]);
}

console.log("whitelist output: ", searchParams.toString() );

How can I make my code check against my whitelist and then run my delete function to remove the junk url args.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
C0nw0nk
  • 870
  • 2
  • 13
  • 29

5 Answers5

3

Explained

Okay, so here's a pretty simple implementation, using reduce function, it's simple, clean and if anything, thanks to using this approach, it doesn't cause for the value(s) of searchParams to change.

Furthermore, I'd like to add that I tried to change nearly as little as possible, I made the assumption that you didn't want side effects in your code.

Edit

If you'd like to understand the ES6-style implementation that I've provided, then you can look more into topics such as currying, for this topic specifically I suggest reading some content produced by Eric Elliott, finally if you want to learn more about the syntax such as arrow functions, I might suggest MDN.

var paramsString = "2=lol&q=how&44=slap&topic=api&1=tr&view=media"; //test url args
var searchParams = new URLSearchParams(paramsString);

// This whitelist of args are the only args to be allowed in the url.
var url_args_whitelist = [
  "beforeafter", "catid", "childforums", "display", "element_id",
  "element_type", "exactname", "filter_mediaType", "filter_order",
  "filter_order_Dir", "filter_search", "filter_tag", "format", "id",
  "Itemid", "layout", "limit", "limitstart", "messageid", "more",
  "option", "order", "ordering", "quality", "query", "recently",
  "recip", "reply_id", "return", "searchdate", "searchf", "searchphrase",
  "searchuser", "searchword", "sortby", "start", "task", "tmpl", "token", "view"
];

// Create an Array from searchParams, then reduce it via ensuring that each 
// key exists within the 'url_args_whitelist' Array, finally joining using 
// an '&' symbol. 
var cleanURL = Array.from(searchParams).reduce(function(array, sub) {
  var key = sub[0], value = sub[1];

  // Check the argument exists in the URL, if so, then push it onto the new array.
  if (url_args_whitelist.indexOf(key) > -1) array.push(key + '=' + value);

  return array;
}, []).join("&");

// Finally a more ES6 style approach, basically a one liner.
const clean = a => l => a.filter(o => l.includes(o[0])).map(o => o.join("=")).join("&");

// Results.
console.log("whitelist output:", cleanURL);
console.log("es6 output:", clean(Array.from(searchParams))(url_args_whitelist));
console.log("old output:", searchParams.toString());
JO3-W3B-D3V
  • 2,124
  • 11
  • 30
  • 1
    Thank you for your soloution! and for thinking ahead about unwanted side effects and bugs with the code i really appreciate that it works flawlessly i have marked your detailed soloution as the answer, All the other answers given do also work but i find yours to be the best for my usage scenario thank you so much. – C0nw0nk Feb 05 '19 at 19:47
3

I would just loop over the array and use reduce to get the keys that you care about. I would not try to delete anything.

var searchParams = new URLSearchParams(paramsString);

var url_args_whitelist = [
"beforeafter",
"catid",
"childforums",
"display"
];

var whiteList = url_args_whitelist.reduce( function (obj, key) {
  var value = searchParams.get(key)
  if (value) {
    obj[key] = value
  }
  return obj;
}, {});

But if you want to keep it with the url params it does have a delete method. So loop over all the entries and than delete it.

searchParams.forEach(function(value, key) {
  if (url_args_whitelist.indexOf(key) === -1) {
    searchParams.delete(key)
  }
});
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • Deleting from an object while iterating it breaks the iteration (it shifts indexes around). Do not follow the second example. – codener Feb 15 '22 at 09:12
2

You have to get your URL, split it in keys and values then filter the keys against the whitelist. In the end you compose the new URL and perform a redirect:

const query = window.location.search.replace('?', '');

const result = query
  .split('&')
  .map(token => {
    const [key, value] = token.split('=');
    return {key, value};
  })
  .filter(keyval => {
    return url_args_whitelist.indexOf(keyval.key) !== -1;
  })
  .map(keyval => {
    return [keyval.key, keyval.value].join('=');
  })
  .join('&');

window.location.search = '?' + result;

Here is a working example:

/* let's suppose these are your query params */

let query = '?childforums=123&abc=345';

query = query.replace('?', '');


var url_args_whitelist = [
"beforeafter",
"catid",
"childforums",
"display",
"element_id",
"element_type",
"exactname",
"filter_mediaType",
"filter_order",
"filter_order_Dir",
"filter_search",
"filter_tag",
"format",
"id",
"Itemid",
"layout",
"limit",
"limitstart",
"messageid",
"more",
"option",
"order",
"ordering",
"quality",
"query",
"recently",
"recip",
"reply_id",
"return",
"searchdate",
"searchf",
"searchphrase",
"searchuser",
"searchword",
"sortby",
"start",
"task",
"tmpl",
"token",
"view"
];



const result = query
  .split('&')
  .map(token => {
    const [key, value] = token.split('=');
    return {key, value};
  })
  .filter(keyval => {
     return url_args_whitelist.indexOf(keyval.key) !== -1;
  })
  .map(keyval => {
    return [keyval.key, keyval.value].join('=');
  })
  .join('&');

console.log('input:', query);
console.log('output:', result);
  • As much as I really like this solution, I don't think that the OP can make sense of this, I'm **assuming** that the OP wouldn't be familiar with things such as arrow functions, or at least provide some sources to documentation? Apart from that, good answer! Clean & concise. – JO3-W3B-D3V Feb 05 '19 at 13:42
  • @JO3-W3B-D3V yeah this is not trivial, but I think that it's a good use of functional programming and we should start to see more code like this. I also tried to keep the code clean, for example using only block functions – Christian Vincenzo Traina Feb 05 '19 at 13:48
  • I couldn't agree more! That's the only reason why I didn't use a similar approach, I assumed the OP wouldn't have been able to make sense of it, possibly, you're totally right, and I personally love that FP is becoming more and more mainstream! +1 from me. – JO3-W3B-D3V Feb 05 '19 at 13:51
  • The problem would be the complexity and performance of the code. You are iterating through the code a lot more times than really necessary. – Dhananjai Pai Feb 05 '19 at 13:54
  • 2
    @DhananjaiPai there is no performance issue on iterating on the same array several times, it just becomes 3*O(n), which si just O(n). If we iterated on it just once and performed all the operations in the same loop, the performance was the same! – Christian Vincenzo Traina Feb 05 '19 at 13:57
  • 2
    3*O(n) may be O(n), but 3*1000 is not 1000. I agree that the code is legible, but it is not optimal :) – Dhananjai Pai Feb 05 '19 at 13:58
  • 2
    This is just theoretically. In practice, javascript performs a better optimization using the old classic for loop, and you can find some publication on the net. But in my opinion, better having a cleaner code than gaining 1ms :) – Christian Vincenzo Traina Feb 05 '19 at 13:59
  • @CristianTraìna Thank you for sharing your soloution it is also a soloution that works i wish i could mark it as the answer with JO3-W3B-D3V also being the correct answer. I have upvoted you for your awesome contribution. – C0nw0nk Feb 05 '19 at 19:44
2

The most optimal way to do it is to store the whitelist urls as an object, instead of an array. Since then, it would act as a hashMap and you would not have to search if the key is included [with O(n) complexity each time]

var paramsString = "2=lol&q=how&44=slap&topic=api&1=tr&view=media"; //test url args
var searchParams = new URLSearchParams(paramsString);

var url_args_whitelist = {
    "topic": true,
    "catid": true,
    // repeat for other values
    "token": true,
    "view": true,
};

var resultParams = new URLSearchParams();

for (let p of searchParams) {
  if (url_args_whitelist[p[0]]) {
    resultParams.append(...p)
  }
}

console.log("whitelist output: ", resultParams.toString() );
Dhananjai Pai
  • 5,914
  • 1
  • 10
  • 25
-1

I guess you are asking for something like this:

   for (let p of searchParams) {
      if (!url_args_whitelist.includes(p[0])) {
        searchParams.delete(p[0]);
      }
    }

    console.log("whitelist output: ", searchParams.toString() );
Aaditya Thakkar
  • 1,750
  • 13
  • 13
  • if you delete in in-place, it affects the iterator and incorrect results would be produced. Run the above code against the question – Dhananjai Pai Feb 05 '19 at 13:51