DeepCode is now part of Snyk. Learn more about Snyk Code.

JavaScript is so sweet tempered. But does it help?

Tuesday, September 29th, 2020

In this post, I want to present an aspect of JavaScript that seems like the language is nice and forgiving at the first glance but is also a source of hard-to-track issues and how DeepCode helps. DeepCode offers an AI-based Static Program Analysis for Java, JavaScript and TypeScript, C/C++, and Python. As you might know, DeepCode uses thousands of open source repos to train our engine.

Let me give you the example first (Btw, it is a real-life example I found here). Try to find the issue:

... // restore saved {block}s for (i=saved.length-1; i>=0; i=i-1) { s = s.replace(new RegExp("~-" + i + "-~"), "{" + saved[i] + "}", "g"); } ...

It is not easy, especially when not being directly pointed at it. Let me give you DeepCode’s suggestion:

replace takes two arguments. Arguments starting with the third one will be ignored.

How JavaScript Function Parameters work

I have to admit it sparked some interest in me. And I researched the underlying facts. You can find it in the ECMAScript standards but I found this nice summary here:

Parameter Rules

  1. JavaScript function definitions do not specify data types for parameters.
  2. JavaScript functions do not perform type checking on the passed arguments.
  3. JavaScript functions do not check the number of arguments received.
  4. JavaScript sets missing parameters to undefined
  5. JavaScript passes parameters (or arguments) by value, e.g. changes to parameters inside a function are not reflected externally. Be aware that complex objects are passed by reference and changes get reflected externally.
  6. From ECMAScript 2015 on, you can provide default values for parameters:
function(a=1, b=2) { /* Function Code */ }

Behind the scenes, JavaScript generates an object called arguments containing the parameters handed to the function. The arguments object has properties reflecting the position of the argument in the parameter list.

{ 0: "First Argument", 1: "Second Argument", 2: "Third Argument" }

Now, you can see why missing arguments end up as undefined: You access a non-existent property in an object and get … undefined. And also how the default value is done.

You can also see what happens to arguments that are overcount and not expected. While they are in the arguments object, they are simply swallowed. In the above’s example, the global flag of the regex will not make a difference.

The upside of the method is that JavaScript can handle a variable number of input parameters.

The downside is that these kind of bugs are really hard to trace. In our example, you literally see the g flag, the runtime throws no error, all is hunky-dory.

DeepCode found this issue by learning the expected layout of parameters for the replace() function as it was changed quite often in open-source repos in lieu of security issues. replace() is often used to treat external data sources such as request strings and so the engine had a special focus on it.

Sweet tempered but does it help?

Coming back to the original heading: All in all, JavaScript is a forgiving language and swallows tons of constructions where the intentions of the developer and the actual effect of the code might differ significantly. Even more dangerous: For Pros using these effects intentionally (as it is safe - mind you it is written in the standard), code might get really hard to understand. Be wary my friends…

Check out how your code is doing and start using DeepCode today.