A Technical Deep Dive into Semgrep’s JavaScript Vulnerability Detection

An in-depth look at Semgrep’s JavaScript vulnerability detection, highlighting critical vulnerabilities found in OSS projects and demonstrating how Semgrep Code uncovers these issues at scale.

Vasilii Ermilov
March 5th, 2025
Your privacy matters to us. By submitting this form, you agree to our Privacy Policy
Share

TL;DR:

  • To enhance JavaScript and TypeScript (JS/TS) security coverage, we made significant improvements to the Semgrep engine, focusing on server-side frameworks where vulnerabilities pose greater risks than client-side issues.

  • We expanded support for Express, Koa, Hapi, and NestJS, incorporating dependency injection tracking, improved module resolution, and better handling of callbacks to reflect real-world JS/TS development patterns.

  • We validated our improvements using security benchmarking repositories like OWASP JuiceShop and BrokenCrystals, but more importantly, we tested our rules at scale by scanning 150+ real-world open-source projects on GitHub providing deeper insights into rule performance in production-level codebases.


Why server-side analysis matters in JavaScript

When it comes to securing web applications, code that is running on the server is exposed to a greater variety of attacks then the code that is executed in the browser of the user. While client-side security issues like Cross-Site Scripting (XSS) are serious, their impact is often limited to individual users. In contrast, server-side vulnerabilities present a much larger attack surface and can lead to far more severe consequences. Vulnerabilities such as SQL injection, Server Side Request Forgery (SSRF) and Remote Code Execution (RCE) can compromise entire systems, exposing sensitive user data and critical business logic. Since the backend of web applications handle authentication, data storage and API logic, a single vulnerability can be exploited at scale, affecting all users of a system. That’s why we decided to focus on expanding Semgrep coverage to secure JavaScript and TypeScript server-side frameworks, in order to make sure that we provide vulnerability detection where it matters most.

To maximize impact, we focused on Node.js runtime since it is the most mature and widespread environment for running JS code on the server. The absolute leader among frameworks is Express, it powers countless web applications, from startups to enterprise systems. On top of that we decided to add support for Koa, Hapi, and NestJS based on the popularity of the projects and direct feedback from our users, who needed better security coverage for their backend applications.

Framework

Stars

Forks

Open Issues

nestjs/nest

69632

7777

49

expressjs/express

66389

17694

179

koajs/koa

35363

3224

26

hapijs/hapi

14669

1345

84

Framework popularity stats (as of 3/1/2025)  

Optimizing Semgrep’s engine for modern JavaScript applications  

In order to improve JavaScript and TypeScript support we had to enrich the Semgrep Pro Engine with language-specific features, enabling it to better understand JS/TS syntax and data flow to accurately detect vulnerabilities.

Callbacks

First of all, we tackled higher-order functions (HOFs) and callbacks, which are widely used in JavaScript. Since functions are frequently passed as arguments or returned as values, tracking tainted data through these patterns is challenging. To eliminate potential false negatives, we enhanced our taint analysis engine to properly handle callbacks and ensure vulnerabilities in such scenarios are not overlooked.


Modules resolution

Another crucial aspect was module resolution in Node.js, which supports both CommonJS and ECMAScript modules. With multiple ways to import dependencies, accurately tracing data flow across files required an upgrade to our engine. We improved module resolution and introduced a new Semgrep pattern that can easily identify the use of specific libraries in code.

  - metavariable-name:

        metavariable: $MIKRO_ORM

        modules:

        - @mikro-orm/core

        - @mikro-orm/mysql



Dependency injection

Besides that we had to do some framework-specific enhancements. For example, NestJS makes extensive use of the Dependency Injection (DI) pattern, which affects how data flows through an application. To accurately detect vulnerabilities in NestJS-based codebases, DI support was added, allowing it to track dependencies and identify data flow across NestJS controllers, services and other modules.

($X: Repository<Cat>).query(...)

Metavariable types are now working for properties that were assigned through dependency injection.

Finally, beyond framework-specific improvements, we also updated engine's taint tracking capabilities, making it more precise in following data flows, including scenarios where JavaScript and TypeScript code is mixed together in one project.




How Semgrep supports 50+ frameworks and libraries

The next step was to develop security rules to detect specific vulnerabilities, ensuring our SAST engine could effectively identify and flag security risks in Node.js applications.

We analyzed popular npm libraries commonly used alongside Express, Koa, Hapi, and NestJS. Our goal was to identify libraries that interact with user input, database queries, file handling, and other critical operations, areas where security misconfigurations can lead to serious vulnerabilities. We focused on those that introduce dangerous sinks, which, if used incorrectly, could expose applications to attacks like SQL injection, command injection, path traversal and others. The full list of currently supported libraries can be found in the Semgrep documentation.

During the rule writing process we focused on covering those OWASP Top 10 vulnerabilities that can be effectively identified using static analysis, such as injection attacks, cryptographic failures, SSRFs and different sorts of security misconfigurations. Most of such rules require taint-tracking analysis, or taint mode as we call it in Semgrep. The sources of taint for web frameworks that we targeted can be mostly described as the various forms of untrusted input that is coming from HTTP requests. We dived deep into the documentation and the source code of web frameworks to correctly identify the sources and when the work was done all we had to do is to connect them to the sinks from the previous paragraph.

While developing our security rules, we leveraged all available benchmarks and testing repositories we could find. Purposefully vulnerable applications created by the community, like OWASP JuiceShop and BrokenCrystals are very helpful when developing rules for Semgrep, as they can give hints on what vulnerabilities and code patterns to cover. Despite the fact that not all of them were built for benchmarking static analysis or practicing source code reviews, we still use them all for continuous testing and fine-tuning of Semgrep rules

True positives from OWASP Juice Shop scan:

Vulnerability Type

File

SQL Injection

/routes/login.ts#L36

SQL Injection

/routes/search.ts#L23

SSRF

/routes/profileImageUrlUpload.ts#L23

NoSQL Injection

/routes/likeProductReviews.ts#L18

/routes/likeProductReviews.ts#L24

/routes/likeProductReviews.ts#L31

/routes/likeProductReviews.ts#L41

NoSQL Injection

/routes/orderHistory.ts#L36

NoSQL Injection

/routes/updateProductReviews.ts#L18

NoSQL Injection

/routes/trackOrder.ts#L17

NoSQL Injection

/routes/showProductReviews.ts#L34

Hardcoded Secret

/models/index.ts#L29

Hardcoded Secret

/master/lib/insecurity.ts#L23

Crypto

/master/lib/utils.ts#L90

Open Redirect

/master/routes/redirect.ts#L19

Code Injection

/master/routes/b2bOrder.ts#L22

True positives from BrokenCrystals scan:

Vulnerability Type

File

SQL Injection

/src/testimonials/testimonials.service.ts#L61

SQL Injection

/src/products/products.service.ts#L56

Command Injection

/src/app.service.ts#L25

Path Traversal

/src/file/file.service.ts#L29

Path Traversal

/src/file/file.controller.ts#L290

XXS

/src/file/file.controller.ts#L291

SSRF

/src/file/cloud.providers.metadata.ts#L271

SSTI

/src/app.controller.ts#L74

XXE

/src/app.controller.ts#L116

XXE

/src/users/users.controller.ts#L540

XPATH Injection

/src/partners/partners.service.ts#L66

Email Injection

/src/email/email.service.ts#L43

Semgrep found real vulnerabilities in OSS projects


On top of that, we believe that just using benchmarks is not enough! That’s why we are also testing our rules using open-source projects from GitHub, which represent a broader range of real-world applications. By analyzing large-scale codebases from diverse OSS projects, we gained a deeper understanding of how our rules behave in various environments and how they interact with complex, production-level code. To achieve that, we fetched over 150 relevant repositories from GitHub that reflect the technological stack we’re targeting, (all of them are built with Express, Koa, Hapi, and NestJS). Throughout the development of our rules, we continuously scanned these projects to gather feedback, identify edge cases, and fine-tune our engine’s detection capabilities. This iterative process allowed us to make data-driven improvements, ensuring that our rules are practical and effective for a wide range of real-world use cases.

Also, as we analyzed these repositories, we were able to identify several vulnerabilities that had the potential to impact the security of the projects. For each relevant finding, we took proactive steps to alert the maintainers—either by submitting pull requests (PRs) with proposed fixes or by directly reaching out to the project owners.

Vulnerabilities detected in OSS projects:

Project

Sink

Vulnerability

bailicangdu/node-elm

/controller/shopping/shop.js#L323

Code Injection

fzaninotto/uptime

/app/api/routes/ping.js#L51

XSS

project-violet/violet

/violet-message-search-client/vms-server/app.ts#L36

Command Injection

CQBoyBrand/Personal-Blog-System

/server/apps/frontend/src/article/article.service.ts#L104

SQL Injection

mybricks/apaas

/platform/server/src/module/apps/apps.controller.ts#L315

Command Injection

superconvert/smart_rtmpd

/web_admin/cmd_utils.js#L335

Path Traversal

epam/UUI

/server/api/RTEDemoApi.js#L14

Path Traversal

lmg-anon/mikupad/

/server/server.js#L285

SQL Injection

project-violet/violet

/violet-message-search-client/vms-server/app.ts#L61

SQL Injection

Loogeek/douban-Vue

/app/controllers/user/user.js#L149-L149

NoSQL Injection

alabid/flylatex

/routes.js#L1145-L1148

NoSQL Injection

dreamerkumar/bizsitegenie

/website/shared/app/controllers/user-group-users.server.controller.js#L81-L81

NoSQL Injection

Conclusion: Stronger security for JavaScript & TypeScript applications

Securing JavaScript and TypeScript applications, particularly on the server side, requires tackling complex code patterns. Through continuous development and refinement and the dedication of our Program Analysis engineers, we improved JS/TS language support for Semgrep to better detect security risks in Express, Koa, Hapi, and NestJS applications. To learn more, watch our in-depth technical webinar and explore Semgrep Code’s documentation for additional details.

About

Semgrep lets security teams partner with developers and shift left organically, without introducing friction. Semgrep gives security teams confidence that they are only surfacing true, actionable issues to developers, and makes it easy for developers to fix these issues in their existing environments.