The role of manually reviewing JavaScript

Charlie Eriksen
Founder

July 29, 2024

Recently, Nathan Jones tweeted a tip for bug bounty hunters, which resonated with me.

 

https://x.com/njcve_/status/1810996262486921688

The point Nathan made also speaks to why I ended up building jswzl in the first place and the design decisions I made, which I’ve not publicly discussed in any detail. So, I figured, why not take this opportunity to share some of the core insights that form the foundation of jswzl? And while I’m here, why not discuss how I approach testing web applications?

Prologue

One of the key traits of my personality for as long as I can remember is an innate curiosity — specifically, I’ve always wanted to understand how things work. When I started my career, this made source code review a very rewarding experience for me as it allowed me to directly see how a system worked. This quickly became one of my favorite tasks since I could find vulnerabilities that would otherwise be time-consuming or challenging to find.

But I have also always loved building things. Reading and understanding code was a crucial part of the design when I started developing the original version of Adversary, a secure coding training platform later acquired by Secure Code Warrior.

And in my spare time, I also built my own automation systems for bug bounties. This is, in fact, how jswzl began.

The origin of jswzl

Having built my bug bounty automation systems over some years, I took the approach of doing thin vertical slices of functionality based on the 80/20 principle, with a stable underlying platform to stitch all the data together into a normalized data structure. A big part of the system is building a “sitemap” based on data from different tools. For instance, when I screenshot a website, I will also capture all requests made and add those to the sitemap.

At some point, I decided to look into adding data from JavaScript. I examined the available open-source tools, and all used Regex. Initially, I tried to see if I could do it better. I realized I preferred to tackle it using static analysis, parsing JavaScript into an Abstract Syntax Tree, and extracting data from that. I integrated it into my tooling, and it worked well!

However, I identified two problems early on:

1. It was important to be able to iterate on the engine quickly, visualize what was going on with a short feedback loop, and use good syntax highlighting.

2. Getting a list of paths, for example, is excellent. However, I needed more important contextual information for testing when manually reviewing the data. I had to manually open the code in Chrome Dev Tools anyway to help inform my testing.

But I wasn’t looking to solve the problem of rendering text on a screen. Tools like Visual Studio Code solved that well. So, I figured: Why not simply write an extension for Visual Studio Code to pull the source code into and visualize it?

This insight was one of a dozen insights that came together to shape what turned into jswzl.

Why manually review JavaScript code?

But why does this matter to people who look for vulnerabilities in software? For me, it comes down to a combination of mindset and a practical approach to testing.

Principle: You should seek to understand an application.

Understanding an application and its functions is an important aspect of any testing. Sure, you can hunt for SQL injection, cross-site scripting, and other vulnerabilities inherent to software systems. However, in my experience in consulting, most vulnerabilities were not identified by using automation.

Instead, they were issues that you could only discover if you understood what the application was made for and how it is meant to work/be used; then, you could find creative ways to use it that weren’t meant to be possible. Many were business logic issues, which you’d never discover without some basic understanding of what the application is doing.

Benefit: Finding hidden functionality

I’ve often discovered functionality in an application that was referenced in the JavaScript source but that I wasn’t able to reach through using the application. These weren’t always obvious from just grepping for paths in the JavaScript source; they required an understanding of how API calls are made in the application.

Benefit: Finding hidden ways of interacting with an API

Like above, I’ve also often found references to special flags used in a Development environment, which would make it possible to query secret data or bypass authentication. Or I’ve found parameters that I could add to a request that would never normally be sent by just using the application. I would never have found these flags or parameters without manually reviewing the source code.

Principle: Identifying patterns

This is one of the more interesting aspects of manual code review. You rapidly start to identify patterns. You’ll often notice code that seems to have been made by different developers, which may contain code smells. Or some part of a system is much older and less maintained. These are often good indicators that you’ll want to focus on specific areas of an application. You can develop a fifth sense of where vulnerabilities may exist.

  

“But isn’t it time-consuming?”

When I’ve discussed manual code review with people in the past, this is one of their common arguments against doing it. There’s a lot of code that isn’t very interesting, so why spend a lot of time reading code that’s not relevant?

The reality is that code review is a learned skill. Initially, you’ll be slow to cover any amount of code, and it’ll be very mentally tiring. However, as with learning how to ride a bicycle, it becomes second nature rather quickly. You will be able to skim through a file at a quick pace, and your intuition will guide you on where to put more conscious focus. Plus:

●       You learn to identify patterns of what’s most important.

●       You learn what code to ignore.

●       You learn the best code review techniques to use in specific situations.

 

And in my opinion, it’s an investment worth making if you’re serious about testing web applications. To actually test something, you also have to understand it. Reading the actual code is the most direct path to doing that.

Jswzl and automation

The principles behind the design and ongoing development of jswzl are grounded in giving people the tools that allow them to focus on the challenges that humans are better suited to tackle. These types of tools are, in effect, force multipliers for a workforce that’s already stretched thin, and we recognize that getting started can be very challenging.

This is not to say that the technology in jswzl is not, and will not be, a part of automation in the future. That’d be ironic, given that it started its life in an automation chain. However, the primary focus is enabling people to do their jobs more effectively. This includes making it possible for people to integrate jswzl into the workflow and tools they are more proficient with, including different types of automation.

When technology makes people more productive, it is possible to scale that up through larger degrees of automation. But that’s only the case if the inputs to that automation are solid to begin with. So, pushing towards larger degrees of integration with automation is something that will happen naturally. Recent releases are taking baby steps towards letting users integrate jswzl into their favorite tools and workflows.

Making an impact, one person at a time.

The belief that a single person can make a meaningful difference in the world, given the right goal and tools to focus their energy on where they can have the most impact. That belief is the foundation of jswzl and continues to influence its direction today. This belief motivated me to quit my job and work on jswzl alone. I saw how much of a difference it made in my workflow. If put in the hands of many others, it could truly move the needle especially as we need all the help we can get to secure the technology systems we rely on daily.